27.Nginx HTTP之监听套接字连接处理函数ngx_http_init_connection

前一篇讲过,对于HTTP指令块下的server,Nginx会为其创建相应的监听套接字,并且注册连接处理函数ngx_http_init_connection。

我们在ngx_event_accept中看到过,该函数会在accept后进行调用。

/* http/ngx_http_request.h */

typedef struct {                                 // HTTP连接结构体定义
    ngx_http_request_t   *request;               // HTTP请求

    ngx_buf_t           **busy;
    ngx_int_t             nbusy;

    ngx_buf_t           **free;
    ngx_int_t             nfree;

    ngx_uint_t            pipeline;
} ngx_http_connection_t;


/* http/ngx_http_request.c */

/* 初始化HTTP请求
 * param rev: HTTP连接的读事件
 */
static void ngx_http_init_request(ngx_event_t *rev)
{
    ngx_uint_t                 i;
    socklen_t                  len;
    struct sockaddr_in         addr_in;
    ngx_connection_t          *c;
    ngx_http_request_t        *r;
    ngx_http_in_port_t        *in_port;
    ngx_http_in_addr_t        *in_addr;
    ngx_http_connection_t     *hc;
    ngx_http_server_name_t    *server_name;
    ngx_http_core_srv_conf_t  *cscf;
    ngx_http_core_loc_conf_t  *clcf;
#if (NGX_HTTP_SSL)
    ngx_http_ssl_srv_conf_t   *sscf;
#endif

    c = rev->data;

    if (rev->timedout) {
        // 如果timedout为1, 表明客户端超时
        
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");

#if (NGX_STAT_STUB)
        (*ngx_stat_reading)--;
#endif
        // 关闭HTTP连接
        ngx_http_close_connection(c);
        return;
    }

    // 从连接c获取其custom data, 在这里也就是HTTP连接
    hc = c->data;

    if (hc) {

#if (NGX_STAT_STUB)
        (*ngx_stat_reading)++;
#endif

    } else {
        // 如果hc为空
        
        // 从内存池c->pool申请一个ngx_http_connection_t结构体大小的内存空间hc
        if (!(hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)))) {

#if (NGX_STAT_STUB)
            (*ngx_stat_reading)--;
#endif

            ngx_http_close_connection(c);
            return;
        }
    }

    // 获取HTTP连接hc的请求r
    r = hc->request;

    if (r) {
        // 如果r不为空
        
        // 对r置全0
        ngx_memzero(r, sizeof(ngx_http_request_t));

        r->pipeline = hc->pipeline;

        if (hc->nbusy) {
            r->header_in = hc->busy[0];
        }

    } else {
        // 如果r为空
        
        // 从内存池c->pool申请一个ngx_http_request_t结构体大小的内存空间r
        if (!(r = ngx_pcalloc(c->pool, sizeof(ngx_http_request_t)))) {

#if (NGX_STAT_STUB)
            (*ngx_stat_reading)--;
#endif

            ngx_http_close_connection(c);
            return;
        }
        // 置hc->request为r
        hc->request = r;
    }

#if (NGX_STAT_STUB)
    r->stat_reading = 1;
#endif

    c->data = r;
    r->http_connection = hc;

    c->sent = 0;
    r->signature = NGX_HTTP_MODULE;

    in_port = c->servers;
    in_addr = in_port->addrs.elts;

    r->port = in_port->port;
    r->port_text = &in_port->port_text;

    i = 0;

    if (in_port->addrs.nelts > 1) {
        // 如果该端口的绑定地址列表的元素个数大于1,
        // 即多宿主机下针对多块网卡绑定该端口

#if (WIN32)
        if (c->local_sockaddr) {
            r->in_addr =
                   ((struct sockaddr_in *) c->local_sockaddr)->sin_addr.s_addr;

        } else {
#endif
            len = sizeof(struct sockaddr_in);
            // 调用getsockname来获取连接的本地地址
            if (getsockname(c->fd, (struct sockaddr *) &addr_in, &len) == -1) {
                ngx_connection_error(c, ngx_socket_errno,
                                     "getsockname() failed");
                ngx_http_close_connection(c);
                return;
            }
            // 记录HTTP请求的本地地址
            r->in_addr = addr_in.sin_addr.s_addr;

#if (WIN32)
        }
#endif

        /* the last in_port->addrs address is "*" */
        // 地址列表的最后一个元素必为通配地址?
        for ( /* void */ ; i < in_port->addrs.nelts - 1; i++) {
            if (in_addr[i].addr == r->in_addr) {
                break;
            }
        }

    } else {
        // 如果该端口的绑定地址列表的元素个数不大于1,
        // 那么HTTP请求的本地地址自然就是in_addr[0].addr
        r->in_addr = in_addr[0].addr;
    }

    r->virtual_names = &in_addr[i].names;

    /* the default server configuration for the address:port */
    cscf = in_addr[i].core_srv_conf;

    r->main_conf = cscf->ctx->main_conf;
    r->srv_conf = cscf->ctx->srv_conf;
    r->loc_conf = cscf->ctx->loc_conf;
    // 更改读事件rev的事件处理函数为ngx_http_process_request_line,
    // 顾名思义, 该函数用于读取和处理请求行
    rev->event_handler = ngx_http_process_request_line;

#if (NGX_HTTP_SSL)
    // 如果编译时, 加入了HTTP_SSL模块

    // 获取ngx_http_ssl_module模块的server层次配置上下文sscf
    sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
    if (sscf->enable) {
        // 如果sscf->enable为1, 即使能HTTPS

        if (c->ssl == NULL) {
            if (ngx_ssl_create_session(sscf->ssl_ctx, c, NGX_SSL_BUFFER)
                                                                  == NGX_ERROR)
            {
                ngx_http_close_connection(c);
                return;
            }

            c->ssl->no_rcv_shut = 1;
            // 设置读事件rev的事件处理函数为ngx_http_ssl_handshake
            rev->event_handler = ngx_http_ssl_handshake;
        }

        r->filter_need_in_memory = 1;
    }

#endif

    server_name = cscf->server_names.elts;
    r->server_name = &server_name->name;

    // 获取ngx_http_core_module模块的location层次配置上下文
    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    c->log->file = clcf->err_log->file;
    if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {
        c->log->log_level = clcf->err_log->log_level;
    }

    if (c->buffer == NULL) {
        // 如果c->buffer为空
        
        // 从内存池c->pool申请一个大小为client_header_buffer_size的临时缓冲区c->buffer
        c->buffer = ngx_create_temp_buf(c->pool,
                                        cscf->client_header_buffer_size);
        if (c->buffer == NULL) {
            ngx_http_close_connection(c);
            return;
        }
    }

    if (r->header_in == NULL) {
        // 如果r->header_in为空, 即HTTP请求存放请求头的缓冲区为空
        
        // 置r->header_in为c->buffer, 也就是使用连接c的buffer来作为请求r的header_in缓冲区
        r->header_in = c->buffer;
    }

    // 创建大小为request_pool_size的内存池r->pool
    if (!(r->pool = ngx_create_pool(cscf->request_pool_size, c->log))) {
        ngx_http_close_connection(c);
        return;
    }

    // 初始化r->cleanup数组: 使用的内存池为r->pool, 元素类型为ngx_http_cleanup_t, 初始容量为5
    if (ngx_array_init(&r->cleanup, r->pool, 5, sizeof(ngx_http_cleanup_t))
                                                                  == NGX_ERROR)
    { 
        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        ngx_http_close_connection(c);
        return;
    }

    // 初始化r->headers_out.headers单向链表: 每个链表节点的数据区: 元素类型为ngx_table_elt_t, 初始容量为20
    if (ngx_list_init(&r->headers_out.headers, r->pool, 20,
                                         sizeof(ngx_table_elt_t)) == NGX_ERROR)
    {
        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        ngx_http_close_connection(c);
        return;
    }

    // 从内存池r->pool申请ngx_http_max_module个指针大小的内存空间r->ctx
    r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);
    if (r->ctx == NULL) {
        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        ngx_http_close_connection(c);
        return;
    }

    c->single_connection = 1;
    r->connection = c;

    r->file.fd = NGX_INVALID_FILE;

    r->headers_in.content_length_n = -1;
    r->headers_in.keep_alive_n = -1;
    r->headers_out.content_length_n = -1;
    r->headers_out.last_modified_time = -1;
    // 置r的http_state为NGX_HTTP_READING_REQUEST_STATE, 即正在读取HTTP请求
    r->http_state = NGX_HTTP_READING_REQUEST_STATE;

#if (NGX_STAT_STUB)
    (*ngx_stat_requests)++;
#endif

    // 调用读事件rev的处理函数来对其进行处理
    // 如果不启用HTTPS, 那么可想而知这里就是调用ngx_http_process_request_line
    rev->event_handler(rev);
}

/* HTTP监听套接字的连接处理函数
 * param c: 待处理的新连接
 */
void ngx_http_init_connection(ngx_connection_t *c)
{
    ngx_event_t         *rev;
    ngx_http_log_ctx_t  *ctx;

    // 日志处理
    if (!(ctx = ngx_pcalloc(c->pool, sizeof(ngx_http_log_ctx_t)))) {
        ngx_http_close_connection(c);
        return;
    }
    ctx->connection = c->number;
    ctx->client = c->addr_text.data;
    ctx->action = "reading client request line";
    c->log->data = ctx;
    c->log->handler = ngx_http_log_error;
    c->log_error = NGX_ERROR_INFO;

    // 获取连接c的读事件rev
    rev = c->read;
    // 设置读事件rev的事件处理函数为ngx_http_init_request
    rev->event_handler = ngx_http_init_request;

    // 设置连接c的写事件的事件处理函数为ngx_http_empty_handler;
    // 顾名思义, ngx_http_empty_handler其实什么也不做, 因为在读取"完"HTTP请求前,
    // 一般不会向客户端发送消息
    /* STUB: epoll edge */ c->write->event_handler = ngx_http_empty_handler;

    if (rev->ready) {
        // 如果rev的ready标志位为1, 即当前可读
        // 当我们设置了deferred accept时, accept后已连接套接字即为可读

        if (ngx_accept_mutex) {
            // 如果ngx_accept_mutex不为空, 说明使用互斥锁来防止惊群现象,
            // 此时把读事件rev放进延迟处理队列等候处理
            
            // 获取ngx_posted_events_mutex锁
            if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) {
                ngx_http_close_connection(c);
                return;
            }
            // 将rev添加进延迟处理队列
            ngx_post_event(rev); 
            // 解锁ngx_posted_events_mutex
            ngx_mutex_unlock(ngx_posted_events_mutex);
            return;
        }

#if (NGX_STAT_STUB)
        // 使能Nginx的统计功能时, ngx_stat_reading所指变量加1
        (*ngx_stat_reading)++;
#endif
        // 直接调用ngx_http_init_request对读事件rev进行处理
        ngx_http_init_request(rev);
        return;
    }

    // 将读事件rev添加到定时器红黑树中, 超时时间为post_accept_timeout;
    // post_accept_timeout顾名思义, 在accept连接后等待第一次读事件的超时时间,
    // 避免客户端连接后长时间不发送请求的问题
    ngx_add_timer(rev, c->listening->post_accept_timeout);

    // 将读事件rev添加到epoll句柄中
    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
        ngx_http_close_connection(c);
        return;
    }

#if 0
    // 因为暂时不会向客户端发送消息, 所以不需要为写事件注册处理函数,
    // 更不需要将写事件添加到epoll句柄
    c->write->ready = 0;
    c->write->event_handler = ngx_http_dummy;

    if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
        ngx_http_close_connection(c);
        return;
    }
#endif

#if (NGX_STAT_STUB)
    (*ngx_stat_reading)++;
#endif

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值