nginx 处理http(发送http响应头数据篇 ----- ngx_http_header_filter_module模块处理)

http状态行
响应头

Server                                //响应server名
Date                                  //响应头日期
Content-Length                        //内容长度
Content-Encoding                      //内容编码格式
Location                              //响应的location头
Last-Modified                         //响应的last-modified头
Accept-Ranges                         //响应的range大小单位类型
Expires                               //响应的expires过期头
Cache-Control                         //Cache-Control响应头
Etag                                  //响应的Etag头

nginx发送http响应头数据的处理流程
1.配置响应头处理filter函数

static ngx_int_t
ngx_http_header_filter_init(ngx_conf_t *cf)
{
    /*设置top_header_filter函数指针 形成链式的top_header_filter处理结构*/
    ngx_http_top_header_filter = ngx_http_header_filter;

    return NGX_OK;
}

2.http响应头数据发送的实际流程

static ngx_int_t
ngx_http_header_filter(ngx_http_request_t *r)
{
...
    if (r->header_sent) {
     /*header_sent为真 说明头部数据已经发送过了 直接返回即可*/
        return NGX_OK;
    }
    /*标记头部数据已经发送*/
    r->header_sent = 1;

    if (r != r->main) {
     /*当前处理的不是主请求 返回 (只有主请求才能发送响应头数据)*/
        return NGX_OK;
    }

    if (r->http_version < NGX_HTTP_VERSION_10) {
       /*只处理在1.0及以上版本的http类型*/
        return NGX_OK;
    }

    if (r->method == NGX_HTTP_HEAD) {
       /*如果请求的方法是head 则只需要发送头部数据即可*/
        r->header_only = 1;
    }

    if (r->headers_out.last_modified_time != -1) {
       /*响应头有last-modified-time信息*/
        if (r->headers_out.status != NGX_HTTP_OK
            && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT
            && r->headers_out.status != NGX_HTTP_NOT_MODIFIED)
        {
        /*响应头不是正常的200/206/304响应码 直接设置last-modified-time为无效*/
            r->headers_out.last_modified_time = -1;
            r->headers_out.last_modified = NULL;
        }
    }
    /*取得响应数据的初始长度*/
    len = sizeof("HTTP/1.x ") - 1 + sizeof(CRLF) - 1
          /* the end of the header */
          + sizeof(CRLF) - 1;

    /* status line */

    if (r->headers_out.status_line.len) {
     /*存在响应状态行信息 增加响应数据长度及获取状态行数据*/
        len += r->headers_out.status_line.len;
        status_line = &r->headers_out.status_line;
#if (NGX_SUPPRESS_WARN)
        status = 0;
#endif

    } else {
    /*如果没有响应状态行数据*/
        status = r->headers_out.status;

        if (status >= NGX_HTTP_OK
            && status < NGX_HTTP_LAST_2XX)
        {
        /*响应的状态码是2XX*/
            /* 2XX */

            if (status == NGX_HTTP_NO_CONTENT) {
             /*状态码表示没有内容 即响应为空*/
                r->header_only = 1;  //标记只有响应头而没有响应的包体
                /*以下的数据无用 设置成无效值*/
                ngx_str_null(&r->headers_out.content_type);
                r->headers_out.last_modified_time = -1;
                r->headers_out.last_modified = NULL;
                r->headers_out.content_length = NULL;
                r->headers_out.content_length_n = -1;
            }
            /*得到状态值的数组索引值*/
            status -= NGX_HTTP_OK;
            /*取得状态行数据*/
            status_line = &ngx_http_status_lines[status];
            /*增加需要发送的数据长度*/
            len += ngx_http_status_lines[status].len;

        } else if (status >= NGX_HTTP_MOVED_PERMANENTLY
                   && status < NGX_HTTP_LAST_3XX)
        { /*如果响应的状态码是3XX*/
            /* 3XX */

            if (status == NGX_HTTP_NOT_MODIFIED) {
                /*304内容无变化 只需要发送响应头即可*/
                r->header_only = 1;
            }
            /*取得状态行的索引*/
            status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX;
            /*得到状态行的数据*/
            status_line = &ngx_http_status_lines[status];
            /*增加需发送的数据长度*/
            len += ngx_http_status_lines[status].len;

        } else if (status >= NGX_HTTP_BAD_REQUEST
                   && status < NGX_HTTP_LAST_4XX)
        {
           /*响应的状态码是4XX*/
            /* 4XX */
            /*得到状态行的索引值*/
            status = status - NGX_HTTP_BAD_REQUEST
                            + NGX_HTTP_OFF_4XX;
            /*取得状态行数据*/
            status_line = &ngx_http_status_lines[status];
            /*增加发送数据的长度*/
            len += ngx_http_status_lines[status].len;

        } else if (status >= NGX_HTTP_INTERNAL_SERVER_ERROR
                   && status < NGX_HTTP_LAST_5XX)
        {
            /*响应的状态码是5XX*/
            /* 5XX */
            /*取得响应状态行的索引值*/
            status = status - NGX_HTTP_INTERNAL_SERVER_ERROR
                            + NGX_HTTP_OFF_5XX;
            /*得到状态行的数据和增加响应数据的长度*/
            status_line = &ngx_http_status_lines[status];
            len += ngx_http_status_lines[status].len;

        } else {
            /*其他响应状态码*/
            len += NGX_INT_T_LEN + 1 /* SP */;
            /*响应的状态行是空的*/
            status_line = NULL;
        }

        if (status_line && status_line->len == 0) {
           /*有响应状态行数据但是长度是0*/
            status = r->headers_out.status;
            len += NGX_INT_T_LEN + 1 /* SP */;
            status_line = NULL;
        }
    }
    /*得到http的location作用域的配置*/
    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (r->headers_out.server == NULL) {
    /*增加响应头的server数据 增加响应数据的长度*/
        len += clcf->server_tokens ? sizeof(ngx_http_server_full_string) - 1:
                                     sizeof(ngx_http_server_string) - 1;
    }

    if (r->headers_out.date == NULL) {
    /*没有设置响应头的日期 就是用unix的初始时间*/
        len += sizeof("Date: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1;
    }

    if (r->headers_out.content_type.len) {
      /*响应头存在content-type 增加响应数据长度*/
        len += sizeof("Content-Type: ") - 1
               + r->headers_out.content_type.len + 2;

        if (r->headers_out.content_type_len == r->headers_out.content_type.len
            && r->headers_out.charset.len)
        {/*响应头有字符集信息 增加需发送的数据长度*/
            len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
        }
    }

    if (r->headers_out.content_length == NULL
        && r->headers_out.content_length_n >= 0)
    {  /*
       没有设置content-length头但是是具有conte-length长度数据
       增加响应数据的长度 会在http响应头中发送content-length头
       */
        len += sizeof("Content-Length: ") - 1 + NGX_OFF_T_LEN + 2;
    }

    if (r->headers_out.last_modified == NULL
        && r->headers_out.last_modified_time != -1)
    {  /*同上*/
        len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1;
    }
    /*取得请求对应的tcp连接*/
    c = r->connection;

    if (r->headers_out.location
        && r->headers_out.location->value.len
        && r->headers_out.location->value.data[0] == '/')
    {   /*响应头存在location数据*/
        r->headers_out.location->hash = 0;

        if (clcf->server_name_in_redirect) {
           /*如果location域配置了redirect 即location重定向*/
            /*取得http server域的配置*/
            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
            /*得到配置的server名称*/
            host = cscf->server_name;

        } else if (r->headers_in.server.len) {
            /*如果请求头有sever信息 使用请求头的sever*/
            host = r->headers_in.server;

        } else {
            /*http没有配置并且请求头没有server信息*/
            host.len = NGX_SOCKADDR_STRLEN;
            host.data = addr;

            if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {
               /*直接使用本地的主机名*/
                return NGX_ERROR;
            }
        }

        switch (c->local_sockaddr->sa_family) {
        /*判断连接的ip协议类型*/

#if (NGX_HAVE_INET6)
        case AF_INET6:
            /*ipv6类型 得到ipv6的地址和端口数据*/
            sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
            port = ntohs(sin6->sin6_port);
            break;
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
        case AF_UNIX:
            /*unix 类型 端口值设置为0*/
            port = 0;
            break;
#endif
        default: /* AF_INET */
            /*ipv4*/
            sin = (struct sockaddr_in *) c->local_sockaddr;
            port = ntohs(sin->sin_port);
            break;
        }
        /*相应的location头的数据长度 增加到响应头数据的长度中*/
        len += sizeof("Location: https://") - 1
               + host.len
               + r->headers_out.location->value.len + 2;

        if (clcf->port_in_redirect) {
        /*http server域配置了端口跳转*/

#if (NGX_HTTP_SSL)
            if (c->ssl)
                /*443-> 0*/
                port = (port == 443) ? 0 : port;
            else
#endif          /*80 -> 0*/
                port = (port == 80) ? 0 : port;

        } else {
            /*默认端口为0*/
            port = 0;
        }

        if (port) {
            /*端口值不为0 增加响应数据长度*/
            len += sizeof(":65535") - 1;
        }

    } else {
       /*没有location响应头 host置为空值 端口也设置成0*/
        ngx_str_null(&host);
        port = 0;
    }

    if (r->chunked) {
        /*数据传输的类型是chunked 增加chunked头的数据长度*/
        len += sizeof("Transfer-Encoding: chunked" CRLF) - 1;
    }

    if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
        /*响应的头状态码为协议切换  增加upgrade类型 connection头数据*/
        len += sizeof("Connection: upgrade" CRLF) - 1;

    } else if (r->keepalive) {
       /*如果请求是keepalive类型的 增加keep-alive的头数据信息*/
        len += sizeof("Connection: keep-alive" CRLF) - 1;

        /*
         * MSIE and Opera ignore the "Keep-Alive: timeout=" header.
         * MSIE keeps the connection alive for about 60-65 seconds.
         * Opera keeps the connection alive very long.
         * Mozilla keeps the connection alive for N plus about 1-10 seconds.
         * Konqueror keeps the connection alive for about N seconds.
         */

        if (clcf->keepalive_header) {
           /*
           http server域配置了keepalive_header 
           增加keep-alive响应头数据 并增加响应数据长度
           */
            len += sizeof("Keep-Alive: timeout=") - 1 + NGX_TIME_T_LEN + 2;
        }

    } else {
       /*没有keep-alive 发送的是close 状态的connection头*/
        len += sizeof("Connection: close" CRLF) - 1;
    }

#if (NGX_HTTP_GZIP)
    if (r->gzip_vary) {
        /*gzip处理*/
        if (clcf->gzip_vary) {
            /*server域配置了 gzip_vary 增加vary响应头数据*/
            len += sizeof("Vary: Accept-Encoding" CRLF) - 1;

        } else {
            /*server域没有配置gzip vary 则设置gzip_vary为假*/
            r->gzip_vary = 0;
        }
    }
#endif 
   /*取得响应头的数组*/
    part = &r->headers_out.headers.part;
    /*取得响应头数组的第一个元素*/
    header = part->elts;

    for (i = 0; /* void */; i++) {
        /*遍历响应头的数组*/

        if (i >= part->nelts) {
            if (part->next == NULL) {
                break;
            }

            part = part->next;
            header = part->elts;
            i = 0;
        }

        if (header[i].hash == 0) {
            continue;
        }
        /*增加响应头的数据长度*/
        len += header[i].key.len + sizeof(": ") - 1 + header[i].value.len
               + sizeof(CRLF) - 1;
    }
    /*建立一个临时的缓冲区用于存放需要的响应数据 len是前面的步骤计算得到的需发送的数据长度值*/
    b = ngx_create_temp_buf(r->pool, len);
    if (b == NULL) {
       /*创建缓冲区失败 直接返回错误*/
        return NGX_ERROR;
    }
    /*以下开始进行响应数据到缓冲区的填充过程*/
    /* "HTTP/1.x " */
    /*先填充http版本信息*/
    b->last = ngx_cpymem(b->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1);

    /* status line */
    /*填充响应行信息*/
    if (status_line) {
        b->last = ngx_copy(b->last, status_line->data, status_line->len);

    } else {
        b->last = ngx_sprintf(b->last, "%03ui ", status);
    }
    /*根据http协议 响应行以\r\n结束*/
    *b->last++ = CR; *b->last++ = LF;
   /*以下是填充响应头部数据的流程*/
    if (r->headers_out.server == NULL) {
       /*填充响应server头*/
        if (clcf->server_tokens) {
            p = (u_char *) ngx_http_server_full_string;
            len = sizeof(ngx_http_server_full_string) - 1;

        } else {
            p = (u_char *) ngx_http_server_string;
            len = sizeof(ngx_http_server_string) - 1;
        }

        b->last = ngx_cpymem(b->last, p, len);
    }

    if (r->headers_out.date == NULL) {
        /*填充响应date头数据*/
        b->last = ngx_cpymem(b->last, "Date: ", sizeof("Date: ") - 1);
        b->last = ngx_cpymem(b->last, ngx_cached_http_time.data,
                             ngx_cached_http_time.len);

        *b->last++ = CR; *b->last++ = LF;
    }

    if (r->headers_out.content_type.len) {
        /*填充content-type头数据*/
        b->last = ngx_cpymem(b->last, "Content-Type: ",
                             sizeof("Content-Type: ") - 1);
        p = b->last;
        b->last = ngx_copy(b->last, r->headers_out.content_type.data,
                           r->headers_out.content_type.len);

        if (r->headers_out.content_type_len == r->headers_out.content_type.len
            && r->headers_out.charset.len)
        {   /*填充charset头数据*/
            b->last = ngx_cpymem(b->last, "; charset=",
                                 sizeof("; charset=") - 1);
            b->last = ngx_copy(b->last, r->headers_out.charset.data,
                               r->headers_out.charset.len);

            /* update r->headers_out.content_type for possible logging */

            r->headers_out.content_type.len = b->last - p;
            r->headers_out.content_type.data = p;
        }
        /*每个http响应头均以\r\n结束 以作区分*/
        *b->last++ = CR; *b->last++ = LF;
    }

    if (r->headers_out.content_length == NULL
        && r->headers_out.content_length_n >= 0)
    {  /*填充 content-length响应头*/
        b->last = ngx_sprintf(b->last, "Content-Length: %O" CRLF,
                              r->headers_out.content_length_n);
    }

    if (r->headers_out.last_modified == NULL
        && r->headers_out.last_modified_time != -1)
    {   /*填充last-modified响应头*/
        b->last = ngx_cpymem(b->last, "Last-Modified: ",
                             sizeof("Last-Modified: ") - 1);
        b->last = ngx_http_time(b->last, r->headers_out.last_modified_time);

        *b->last++ = CR; *b->last++ = LF;
    }

    if (host.data) {
       /*填充location响应头*/
        p = b->last + sizeof("Location: ") - 1;

        b->last = ngx_cpymem(b->last, "Location: http",
                             sizeof("Location: http") - 1);

#if (NGX_HTTP_SSL)
        if (c->ssl) {
            *b->last++ ='s';
        }
#endif

        *b->last++ = ':'; *b->last++ = '/'; *b->last++ = '/';
        b->last = ngx_copy(b->last, host.data, host.len);

        if (port) {
            b->last = ngx_sprintf(b->last, ":%ui", port);
        }

        b->last = ngx_copy(b->last, r->headers_out.location->value.data,
                           r->headers_out.location->value.len);

        /* update r->headers_out.location->value for possible logging */

        r->headers_out.location->value.len = b->last - p;
        r->headers_out.location->value.data = p;
        ngx_str_set(&r->headers_out.location->key, "Location");

        *b->last++ = CR; *b->last++ = LF;
    }

    if (r->chunked) {
       /*
       chunked传输类型 填充transfer-encoding响应头 
       当然类型是chunked
       */
        b->last = ngx_cpymem(b->last, "Transfer-Encoding: chunked" CRLF,
                             sizeof("Transfer-Encoding: chunked" CRLF) - 1);
    }
    /*根据状态码及keepalive配置 填充connection响应头*/
    if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
        b->last = ngx_cpymem(b->last, "Connection: upgrade" CRLF,
                             sizeof("Connection: upgrade" CRLF) - 1);

    } else if (r->keepalive) {
        b->last = ngx_cpymem(b->last, "Connection: keep-alive" CRLF,
                             sizeof("Connection: keep-alive" CRLF) - 1);

        if (clcf->keepalive_header) {
            b->last = ngx_sprintf(b->last, "Keep-Alive: timeout=%T" CRLF,
                                  clcf->keepalive_header);
        }

    } else {
        b->last = ngx_cpymem(b->last, "Connection: close" CRLF,
                             sizeof("Connection: close" CRLF) - 1);
    }

#if (NGX_HTTP_GZIP)
    if (r->gzip_vary) {
        /*填充gzip_vary的 vary响应头*/
        b->last = ngx_cpymem(b->last, "Vary: Accept-Encoding" CRLF,
                             sizeof("Vary: Accept-Encoding" CRLF) - 1);
    }
#endif
    /*遍历响应头的 数组*/
    part = &r->headers_out.headers.part;
    header = part->elts;

    for (i = 0; /* void */; i++) {

        if (i >= part->nelts) {
            if (part->next == NULL) {
                break;
            }

            part = part->next;
            header = part->elts;
            i = 0;
        }

        if (header[i].hash == 0) {
            continue;
        }
        /*将响应头数组中的头部信息填充到缓冲区中*/
        b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
        *b->last++ = ':'; *b->last++ = ' ';

        b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);
        *b->last++ = CR; *b->last++ = LF;
    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                   "%*s", (size_t) (b->last - b->pos), b->pos);

    /* the end of HTTP header */
    /*头部处理收尾工作*/
    *b->last++ = CR; *b->last++ = LF;
    /*设置响应头的大小*/
    r->header_size = b->last - b->pos;

    if (r->header_only) {
       /*只需要响应头部数据 将这个缓冲区设置为最后一个*/
        b->last_buf = 1;
    }

    out.buf = b;
    out.next = NULL;
    /*将填充完成的缓冲区提交到write_filter write_filter处理后 会将数据发送到请求端*/
    return ngx_http_write_filter(r, &out);
}

你可能感兴趣的:(nginx,web,web缓存服务器,后端,http协议)