Nginx源码分析 - HTTP模块篇 - HTTP Request解析过程(22)

GitLab私有化部署全链路实操:Ubuntu+nginx+MySQL生产级落地 GitLab作为企业级DevOps平台,其私有化部署远不止安装软件,而是涉及操作系统适配、反向代理深度配置与数据库兼容性治理的技术体系。核心原理在于理解GitLab Omnibus包对Ubuntu 22.04 LTS的ABI依赖、nginx对Git over HTTP(S)和WebSocket的协议级支持要求,以及MySQL替代PostgreSQL时的严格模式与功能取舍。该方案具备高可控性、审计合规性与内网安全价值,广泛应用于金融、政务、制造业等对代码不出域、日志留存、物理隔离有强约束的场景,是构建自主可控 阅读详情
AI摘要

本文详细介绍了Nginx中HTTP请求的解析流程,包括请求行、头部和请求体的处理过程,以及核心处理函数ngx_http_handler的工作原理。

目录

一、HTTP Request解析流程图

二、源码分析

1. 等待read事件ngx_http_wait_request_handler

2. 处理头部请求行ngx_http_process_request_line

3. 处理HTTP协议的header部分ngx_http_process_request_headers

4. 处理request信息ngx_http_process_request

5. 核心分发函数ngx_http_handler


上一章我们讲解了整个Nginx的tcp连接过程,我们知道当客户端刚连接上来的时候,会触发ngx_event_accept方法;当read事件进来的时候,就会调用ngx_http_wait_request_handler。ngx_http_wait_request_handler方法也是http模块数据处理的入口。

这一章,我们从ngx_http_wait_request_handler函数讲起,深入整个http的request的解析过程。nginx文件:http/ngx_http_request.c

一、HTTP Request解析流程图


说明:

  • Nginx的HTTP核心模块只解析request的请求行和请求头,不会主动读取HTTP 请求body数据,但是提供了ngx_http_read_client_request_body方法,供各个filter模块处理。
  • ngx_http_wait_request_handler:等待read事件上来,并且等到HTTP的request数据
  • ngx_http_process_request_line:处理HTTP的request的请求行
  • ngx_http_process_request_header:处理HTTP的request的请求头
  • ngx_http_handler:HTTP核心处理函数,模块filter链的分发函数
  • 设置r->write_event_handler = ngx_http_core_run_phases,Nginx的write事件模块,从ngx_http_core_run_phases方法开始

为什么不主动读取HTTP BODY数据?

看到上面的图,可能很多人会比较奇怪了,为什么HTTP的核心模块只读取了HTTP协议的请求行和请求头,而没有读取HTTP的请求body内容。

因为大部分情况下,HTTP协议是不需要用到HTTP body中的数据的,例如你返回一张图片或者一个静态页面。

只有在需要将body数据传输到后端的例如JAVA、PHP等的时候,才会需要将HTTP的body数据带过去(POST请求、文件上传等)。所以Nginx只有在特殊的模块下(比如proxy模块,fastcgi模块,uwsgi模块等),才会去主动调用ngx_http_read_client_request_body方法,将body传递到后端。

二、源码分析


1. 等待read事件ngx_http_wait_request_handler


等待read事件上来,并且等到HTTP的request数据。

ngx_http_wait_request_handler主要是一个等待数据到来的功能。里面有一个设计亮点:此函数会一直等待TCP管道中的数据,如果触发了read事件,但是没有读取到数据,则Nginx会将buf内存删除,然后继续等待read事件的到来,好处是防止大量非法请求上来,又占用内存不释放,导致Nginx内存暴涨。

/**
 * read事件回调函数;主要处理读取事件
 * 等待处理http request 数据
 */
static void
ngx_http_wait_request_handler(ngx_event_t *rev)
{
    u_char                    *p;
    size_t                     size;
    ssize_t                    n;
    ngx_buf_t                 *b;
    ngx_connection_t          *c;
    ngx_http_connection_t     *hc;
    ngx_http_core_srv_conf_t  *cscf;

    /* 从rev中获取连接 ngx_connection_t 对象 */
    c = rev->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler");

    /* 客户端超时 */
    if (rev->timedout) {
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
        ngx_http_close_connection(c);
        return;
    }

    /* 客户端连接关闭 */
    if (c->close) {
        ngx_http_close_connection(c);
        return;
    }

    /* 获取sev的conf配置 ngx_http_core_srv_conf_t*/
    hc = c->data;
    cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);

    /* 每次读取数据的buf大小 */
    size = cscf->client_header_buffer_size;

    /* ngx_connection_s 中的buffer:用于接收和缓存客户端发来的字符流 */
    b = c->buffer;

    /* 如果buffer为null了,则直接创建一个临时buf ngx_create_temp_buf*/
    if (b == NULL) {
        b = ngx_create_temp_buf(c->pool, size);
        if (b == NULL) {
            ngx_http_close_connection(c);
            return;
        }

        c->buffer = b;

    /* 如果buf的start为null,则直接创建一块内存,并且调整ngx_buf_t的姿势 */
    } else if (b->start == NULL) {

        b->start = ngx_palloc(c->pool, size);
        if (b->start == NULL) {
            ngx_http_close_connection(c);
            return;
        }

        b->pos = b->start;
        b->last = b->start;
        b->end = b->last + size;
    }

    /**
     * 接收数据的回调函数:os/ngx_recv.c文件夹中的 ngx_unix_recv
     * ngx_unix_recv:主要调用系统的recv函数,循环接收TCP管道中的数据
     */
    n = c->recv(c, b->last, size);

    /*
     * NGX_AGAIN
     * 这个是一个Http连接第一次等待读取数据,如果第一次接收的数据为空,则表示当前客户端连接上来了,但是数据还未上来,
     * 则将当前连接上的读事件添加到定时器机制中,同时将读事件注册到epoll 事件机制中,return 从当前函数返回
     * */
    if (n == NGX_AGAIN) {

        if (!rev->timer_set) {
            ngx_add_timer(rev, c->listening->post_accept_timeout);
            ngx_reusable_connection(c, 1);
        }

        /* 重新把读事件注册到事件中,每次epoll_wait后,fd的事件类型将会清空,需要再次注册读写事件 */
        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
            ngx_http_close_connection(c);
            return;
        }

        /*
         * We are trying to not hold c->buffer's memory for an idle connection.
         * 因为啥也不做,暂时现将buf清空
         */
        if (ngx_pfree(c->pool, b->start) == NGX_OK) {
            b->start = NULL;
        }

        return;
    }

    /* 读取错误 ,则关闭连接 */
    if (n == NGX_ERROR) {
        ngx_http_close_connection(c);
        return;
    }

    /* 客户端主动关闭连接 */
    if (n == 0) {
        ngx_log_error(NGX_LOG_INFO, c->log, 0,
                      "client closed connection");
        ngx_http_close_connection(c);
        return;
    }

    /* 真正读取到数据后的处理 */
    b->last += n;

    if (hc->proxy_protocol) {
        hc->proxy_protocol = 0;

        p = ngx_proxy_protocol_read(c, b->pos, b->last);

        if (p == NULL) {
            ngx_http_close_connection(c);
            return;
        }

        b->pos = p;

        if (b->pos == b->last) {
            c->log->action = "waiting for request";
            b->pos = b->start;
            b->last = b->start;
            ngx_post_event(rev, &ngx_posted_events);
            return;
        }
    }

    c->log->action = "reading client request line";

    ngx_reusable_connection(c, 0);

    /* 调用 ngx_http_create_request 方法构造ngx_http_request_t 请求结构体,并设置到当前连接的data 成员 */
    c->data = ngx_http_create_request(c);
    if (c->data == NULL) {
        ngx_http_close_connection(c);
        return;
    }

    /* 设置当前读事件的回调方法为 ngx_http_process_request_line
     * 并执行该回调方法开始接收并解析HTTP头部的请求行 */
    rev->handler = ngx_http_process_request_line;
    ngx_http_process_request_line(rev);
}

2. 处理头部请求行ngx_http_process_request_line


ngx_http_process_request_line:主要用来处理HTTP协议的请求头。

/**
 * read事件的时候,读取和处理HTTP头部的行
 * 请求行如下:
 * POST /index.php HTTP/1.1
 */
static void
ngx_http_process_request_line(ngx_event_t *rev)
{
    ssize_t              n;
    ngx_int_t            rc, rv;
    ngx_str_t            host;
    ngx_connection_t    *c;
    ngx_http_request_t  *r;


    c = rev->data; //ngx_connection_t
    r = c->data; //ngx_http_request_t

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
                   "http process request 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;
    }

    rc = NGX_AGAIN;
    /* 循环处理请求 NGX_AGAIN为标识服务*/
    for ( ;; ) {

        if (rc == NGX_AGAIN) {

        	/* 读取请求数据  也会调用方法:os/ngx_recv.c文件夹中的 ngx_unix_recv*/
            n = ngx_http_read_request_header(r);

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

        /*
         * 如果数据接收已经完整了,则对HTTP请求行进行 parse解析
         * */
        rc = ngx_http_parse_request_line(r, r->header_in);

        /* 如果解析成功 */
        if (rc == NGX_OK) {

            /* the request line has been parsed successfully */

            r->request_line.len = r->request_end - r->request_start;
            r->request_line.data = r->request_start;
            r->request_length = r->header_in->pos - r->request_start;

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                           "http request line: \"%V\"", &r->request_line);

            r->method_name.len = r->method_end - r->request_start + 1;
            r->method_name.data = r->request_line.data;

            if (r->http_protocol.data) {
                r->http_protocol.len = r->request_end - r->http_protocol.data;
            }

            if (ngx_http_process_request_uri(r) != NGX_OK) {
                return;
            }

            if (r->host_start && r->host_end) {

                host.len = r->host_end - r->host_start;
                host.data = r->host_start;

                rc = ngx_http_validate_host(&host, r->pool, 0);

                if (rc == NGX_DECLINED) {
                    ngx_log_error(NGX_LOG_INFO, c->log, 0,
                                  "client sent invalid host in request line");
                    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
                    return;
                }

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

                if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {
                    return;
                }

                r->headers_in.server = host;
            }

            if (r->http_version < NGX_HTTP_VERSION_10) {

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

                ngx_http_process_request(r);
                return;
            }


            if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
                              sizeof(ngx_table_elt_t))
                != NGX_OK)
            {
                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                return;
            }

            c->log->action = "reading client request headers";

            /* 请求行解析成功后,将read事件的回调函数设置为:ngx_http_process_request_headers
             * ngx_http_process_request_headers:用于处理http的header数据*/
            rev->handler = ngx_http_process_request_headers;
            ngx_http_process_request_headers(rev);

            return;
        }

        /* 如果ngx_http_parse_request_line解析结果有误,则ngx_http_finalize_request 析构request结构*/
        if (rc != NGX_AGAIN) {

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

            ngx_log_error(NGX_LOG_INFO, c->log, 0,
                          ngx_http_client_errors[rc - NGX_HTTP_CLIENT_ERROR]);
            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
            return;
        }

        /* NGX_AGAIN: a request line parsing is still incomplete */

        /* 如果解析的header头部非常大,则调用ngx_http_alloc_large_header_buffer处理buffer*/
        if (r->header_in->pos == r->header_in->end) {

            rv = ngx_http_alloc_large_header_buffer(r, 1);

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

            if (rv == NGX_DECLINED) {
                r->request_line.len = r->header_in->end - r->request_start;
                r->request_line.data = r->request_start;

                ngx_log_error(NGX_LOG_INFO, c->log, 0,
                              "client sent too long URI");
                ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE);
                return;
            }
        }
    }
}

ngx_http_read_request_header:主要调用系统的recv函数,循环接收TCP管道中的数据,接收数据的回调函数:os/ngx_recv.c文件夹中的 ngx_unix_recv

/**
 * 读取请求数据
 */
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) {
        return n;
    }

    /**
     * 接收数据的回调函数:os/ngx_recv.c文件夹中的 ngx_unix_recv
     * ngx_unix_recv:主要调用系统的recv函数,循环接收TCP管道中的数据
     */
    if (rev->ready) {
        n = c->recv(c, r->header_in->last,
                    r->header_in->end - r->header_in->last);
    } else {
        n = NGX_AGAIN; //如果还未可以读取,则返回NGX_AGAIN 继续等待数据到来
    }

    /* 如果是等待状态,则继续放进事件event循环中*/
    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);
        }

        /* 重新把读事件注册到事件中,每次epoll_wait后,fd的事件类型将会清空,需要再次注册读写事件 */
        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");
    }

    /* 客户端关闭连接或者请求错误,则对request进行析构处理*/
    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;
}

3. 处理HTTP协议的header部分ngx_http_process_request_headers


ngx_http_process_request_headers:主要用于解析HTTP头部的header部分数据。例如:

Host: localhost
Accept-Language: zh-cn,zh;q=0.5

/**
 * 用于处理http的header数据
 * 请求头:
 * Host: localhost
 * User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2
 * Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
 * Accept-Language: zh-cn,zh;q=0.5
 * Accept-Encoding: gzip, deflate
 */
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;
    }

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    rc = NGX_AGAIN;

    for ( ;; ) {

        if (rc == NGX_AGAIN) {

        	/* 如果buf太小,则扩大buf */
            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;
                }
            }

            /* 读取请求数据  调用方法:os/ngx_recv.c文件夹中的 ngx_unix_recv */
            n = ngx_http_read_request_header(r);

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

        /* the host header could change the server configuration context */
        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

        /* 处理HTTP请求头数据 */
        rc = ngx_http_parse_header_line(r, r->header_in,
                                        cscf->underscores_in_headers);

        /* 处理成功,则继续解析下一个http header数据 */
        if (rc == 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;
        }

        /* 如果HTTP的header全部处理*/
        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {

            /* a whole header has been parsed successfully */

            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;

            /* 检查解析后的request header是否符合要求,如果不符合要求,返回NGX_ERROR */
            rc = ngx_http_process_request_header(r);

            if (rc != NGX_OK) {
                return;
            }

            /* 请求行和请求头处理完成后,调用ngx_http_process_request,处理HTTP请求body以及响应 */
            ngx_http_process_request(r);

            return;
        }

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

4. 处理request信息ngx_http_process_request


ngx_http_process_request:设置read和write的回调函数ngx_http_request_handler,ngx_http_request_handler通过状态机来判断是读事件还是写事件。

调用HTTP模块的filter链,filter链分为header和body链两部分

/**
 * 
 */
void
ngx_http_process_request(ngx_http_request_t *r)
{
    ngx_connection_t  *c;

    c = r->connection;

#if (NGX_HTTP_SSL)

    if (r->http_connection->ssl) {
        long                      rc;
        X509                     *cert;
        ngx_http_ssl_srv_conf_t  *sscf;

        if (c->ssl == NULL) {
            ngx_log_error(NGX_LOG_INFO, c->log, 0,
                          "client sent plain HTTP request to HTTPS port");
            ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);
            return;
        }

        sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);

        if (sscf->verify) {
            rc = SSL_get_verify_result(c->ssl->connection);

            if (rc != X509_V_OK
                && (sscf->verify != 3 || !ngx_ssl_verify_error_optional(rc)))
            {
                ngx_log_error(NGX_LOG_INFO, c->log, 0,
                              "client SSL certificate verify error: (%l:%s)",
                              rc, X509_verify_cert_error_string(rc));

                ngx_ssl_remove_cached_session(sscf->ssl.ctx,
                                       (SSL_get0_session(c->ssl->connection)));

                ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR);
                return;
            }

            if (sscf->verify == 1) {
                cert = SSL_get_peer_certificate(c->ssl->connection);

                if (cert == NULL) {
                    ngx_log_error(NGX_LOG_INFO, c->log, 0,
                                  "client sent no required SSL certificate");

                    ngx_ssl_remove_cached_session(sscf->ssl.ctx,
                                       (SSL_get0_session(c->ssl->connection)));

                    ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT);
                    return;
                }

                X509_free(cert);
            }
        }
    }

#endif

    if (c->read->timer_set) {
        ngx_del_timer(c->read);
    }

#if (NGX_STAT_STUB)
    (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
    r->stat_reading = 0;
    (void) ngx_atomic_fetch_add(ngx_stat_writing, 1);
    r->stat_writing = 1;
#endif

    /* read和write事件都设置为:ngx_http_request_handler,通过事件状态来判断*/
    c->read->handler = ngx_http_request_handler;
    c->write->handler = ngx_http_request_handler;
    r->read_event_handler = ngx_http_block_reading;

    /* http处理分发核心函数 */
    ngx_http_handler(r);

    ngx_http_run_posted_requests(c);
}

ngx_http_request_handler:通过事件状态来判断读或者写事件,然后调用回调函数

/**
 * read和write事件都设置为:ngx_http_request_handler,通过事件状态来判断
 */
static void
ngx_http_request_handler(ngx_event_t *ev)
{
    ngx_connection_t    *c;
    ngx_http_request_t  *r;

    c = ev->data;
    r = c->data;

    ngx_http_set_log_request(c->log, r);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                   "http run request: \"%V?%V\"", &r->uri, &r->args);

    if (ev->write) {
        r->write_event_handler(r);

    } else {
        r->read_event_handler(r);
    }

    /* 主要用于处理subrequest 子请求 */
    ngx_http_run_posted_requests(c);
}

ngx_http_run_posted_requests:主要用于子请求,子请求会将请求链内容合并到主请求上。会损耗一些性能,因为会重新走一遍write的回调函数ngx_http_core_run_phases解析过程。

例如location配置:

location /main.htm {
    # content of main.htm: main
    add_before_body /hello.htm;
    add_after_body /world.htm;
}
location /hello.htm {
    #content of hello.htm: hello
}
location /world.htm {
    #content of world.htm: world
}
/**
 * 主要用于处理subrequest 子请求
 *
 * location /main.htm {
 *   # content of main.htm: main
 *   add_before_body /hello.htm;
 *   add_after_body /world.htm;
 *	}
 *	location /hello.htm {
 *   	#content of hello.htm: hello
 *	}
 *	location /world.htm {
 *   	#content of world.htm: world
 *	}
 *
 */
void
ngx_http_run_posted_requests(ngx_connection_t *c)
{
    ngx_http_request_t         *r;
    ngx_http_posted_request_t  *pr;

    /* 遍历 */
    for ( ;; ) {

    	/* 如果连接已经销毁,则退出 */
        if (c->destroyed) {
            return;
        }

        r = c->data;
        pr = r->main->posted_requests;

        if (pr == NULL) {
            return;
        }

        r->main->posted_requests = pr->next;

        r = pr->request;

        ngx_http_set_log_request(c->log, r);

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "http posted request: \"%V?%V\"", &r->uri, &r->args);

        /* 调用write handler.   */
        r->write_event_handler(r);
    }
}

5. 核心分发函数ngx_http_handler


ngx_http_handler函数主要用于设置write事件回调函数。r->write_event_handler = ngx_http_core_run_phases

/**
 * 核心分发函数,主要用于设置write写事件回调函数ngx_http_core_run_phases
 */
void
ngx_http_handler(ngx_http_request_t *r)
{
    ngx_http_core_main_conf_t  *cmcf;

    r->connection->log->action = NULL;

    if (!r->internal) {
        switch (r->headers_in.connection_type) {
        case 0:
            r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);
            break;

        case NGX_HTTP_CONNECTION_CLOSE:
            r->keepalive = 0;
            break;

        case NGX_HTTP_CONNECTION_KEEP_ALIVE:
            r->keepalive = 1;
            break;
        }

        r->lingering_close = (r->headers_in.content_length_n > 0
                              || r->headers_in.chunked);
        r->phase_handler = 0;

    } else {
        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
        r->phase_handler = cmcf->phase_engine.server_rewrite_index;
    }

    r->valid_location = 1;
#if (NGX_HTTP_GZIP)
    r->gzip_tested = 0;
    r->gzip_ok = 0;
    r->gzip_vary = 0;
#endif

    /* 设置write事件回调函数,并且执行ngx_http_core_run_phases回调函数 */
    r->write_event_handler = ngx_http_core_run_phases;
    ngx_http_core_run_phases(r);
}

 

2026年Java开发首选:Spring Boot核心原理与工程实践 Spring Boot作为Java生态中事实标准的工程化框架,其本质是通过‘约定优于配置’和条件化自动装配机制,大幅降低企业级应用的基础设施复杂度。它将Servlet容器、数据访问、JSON序列化等能力封装为可插拔Starter,使开发者聚焦业务逻辑而非环境适配。在云原生与快速交付成为主流的2026年,Spring Boot凭借开箱即用的生产就绪特性(如Actuator监控、Profile多环境管理、GraalVM原生支持)持续巩固其不可替代地位。本文深入解析自动装配源码机制、Starter设计范式及app 阅读详情

相关推荐

全面解读Spring Cloud Zuul:从配置到优化的实战指南

在微服务架构中,API网关作为核心组件之一,承担着请求路由、负载均衡、安全认证等重要功能。Spring Cloud Zuul作为一款功能强大的API网关解决方案,得到了广泛应用。本文将深入探讨Spring Cloud Zuul的各项功能,从基础配置到工作原理,再到多层负载和应用优化,全面解析其在实际应用中的最佳实践与实用技巧,为开发者提供一站式指导,助力其打造高性能、高可用的微服务架构。

曾经“等你生日那天”都遥远得像未来,如今却可欢愉的挥手说“下个十年见” 169万+

nginx 源码分析:http 请求处理流程——读取请求

ngx_http_wait_request_handler 当客户端发送的请求到达时,epoll 监测到了一个读事件,然后调用读事件的 handler ngx_http_wait_request_handler 函数处理。它的原型如下: static void ngx_http_wait_request_handler(ngx_event_t *rev); 函数开始 c = rev-&g...

alenliu's blog 2075

电商微信小程序源码全栈解析:从Spring Boot后端到部署与二次开发实战

在软件开发领域,源码分析与全栈部署是开发者从理论走向实践的关键环节。其核心原理在于理解一个完整应用从前端界面、后端逻辑到数据库设计的协同工作方式,掌握如何将分散的代码模块集成为一个可运行的系统。这项技术的价值在于能大幅降低项目启动门槛,使开发者能基于成熟框架快速构建可定制化的商业应用,尤其适用于电商、社交等需要快速验证和迭代的场景。本文聚焦于电商微信小程序这一典型应用,深入拆解一套包含Spring Boot后端和MySQL数据库的完整源码架构。通过剖析其模块设计、数据流与部署流程,并结合微信支付集成、库存管

weixin_34413103的博客 455

三、ngx_lua部分说明

在nginx.conf中添加如下URL get_random_string.lua 结果 要说明的: ngx.HTTP_BAD_REQUEST的参数在ngx_lua中有常量的定义,因此这里可以这样写 ngx_lua 官方文档https://github.com/openresty/lua-nginx-module/blob/master/README.m...

Shawn的博客 339

基于python的电商评论文本分析

本文基于京东平台安井手抓饼2000余条评论数据,运用Python文本分析技术(包括分词、词频统计、LDA主题模型等)探究消费者评价特征。研究发现:1)产品评价聚焦口味("好吃")、性价比("一分钱")和便捷性("早餐");2)京东物流服务获得高度认可;3)销售旺季集中在11月至次年1月。分析揭示了速冻食品消费者的核心关注点,为商家优化产品与服务提供数据支持,同时展示了文本挖掘技术在电商评论分析中的实用价值。

weixin_50902344的博客 8347

Foxglove 介绍以及入门使用教程

Foxglove 是由Foxglove 工作室开发的一套工具集,包括和等组件。跨平台支持:可以在 Windows、macOS 和 Linux 上运行。多协议兼容:支持 ROS、ROS 2、CAN 数据以及其他自定义协议。丰富的可视化功能:可以绘制点云、轨迹、激光雷达数据、IMU 数据等。实时与离线模式:既可以实时监控机器人状态,也可以加载和回放记录的数据文件。Foxglove 的 3D 视图允许你以三维方式查看机器人的运动轨迹、传感器数据和其他几何信息。你可以旋转、缩放和平移视图以获得更好的观察角度。

zolty 1万+

Nginx开发HTTP模块(五):处理用户请求

本文介绍如何处理一个实际的HTTP请求。

Ivan45007的博客 1312

C# 开发 ACS 运动控制系统:从入门到高阶应用实践

起始位置</param>/// <param name="endPos">结束位置</param>/// <param name="maxVelocity">最大速度</param>/// <param name="maxAcceleration">最大加速度</param>/// <param name="maxJerk">最大加加速度

品质生活,极致编程。 2842

android usb 异步请求理解

先贴这个adbtest的例子,用android的usb sdk传输adb协议数据,将android的logcat信息打印再屏幕上。 我研究这个例子的目的是为了实现usb传输tcpip数据,需要借用adb。 http://www.java2s.com/Code/Android/Hardware/USBdevicethatsupportstheadbprotocol.htm 这例子具体有几个类:a

anribras的专栏 1768

TIA博途中通过UDT实现IO地址映射到DB块中的具体方法

TIA博途中通过UDT实现IO地址映射到DB块中的具体方法

Robot_PLC_HMI_Automation 1万+

开源遥感语义分割工具箱mmsegmentation基于Lovedata数据集训练模型

开源遥感语义分割工具箱mmsegmentation基于Lovedata数据集训练模型

yilvyangguang520的博客 498

nginx中request请求的解析

ngx_http_init_request 中初始化event 的handler 为ngx_http_process_request_line,然后首先调用ngx_http_read_request_header来读取头部,然后就是开始调用函数ngx_http_parse_request_line对request line进行解析。随后如果解析的url是complex的话,就进入complex...

但行好事 莫问前程 1117

高通平台N4单路输入的720P相机预览

N4的一路720P Camera作为MIPI摄像头使用

llw21680的专栏 165

Nginx源码分析 - HTTP模块篇 - HTTP Request解析过程(22)

目录 一、HTTP Request解析流程图 二、源码分析 1. 等待read事件ngx_http_wait_request_handler 2. 处理头部请求行ngx_http_process_request_line 3. 处理HTTP协议的header部分ngx_http_process_request_headers 4. 处理request信息ngx_http_process_request 5. 核心分发函数ngx_http_handler 上一章我们讲解了整个Nginx的tcp

alpha_love的博客 1171

倒立摆的PLC控制(模型分析+ SCL源代码)

首先简单介绍下倒立摆模型,下面这幅图是MATLAB网站上的倒立摆模型,下面我们利用牛顿第二运动定律建立摆杆的运动方程。

RXXW_Dor的博客 3332

SWC_RTE01-AutoSAR架构【AutoSAR架构设计概述】

1 基于AutoSAR架构的概述 1.1架构层面:功能可以细分子功能,各个子功能需要交互。 -大功能可以通过Autosar组件Composition去表示; -各个子功能一般具有不可分割性,可以通过Autosar原子组件Component去表示; -各个子功能的交互可以通过接口去通信和交互; -各个子功能的行为可以通过内部行为去表示;具体表现为Runnable的设计;架构设计的时候不考虑具体的逻辑实现;只会关注Ruannble的触发事件或周期;数据访问的一致性问题;具体逻辑设计可通过模版c文件实现或通过基于

8451

nginx(七十七)nginx与包体的探究

nginx与body体。

wzj_110的博客 1177

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

freehire 的 Gupy 看板发现机制:harvest-boards 的 discoverer 接口与活体验证管线

这篇指南围绕 freehire 仓库中一次已归档的变更提案(Gupy board discovery)展开:它讲清楚了 `cmd/harvest-boards` 这个宿主工具为什么原本只能靠"种子列表"扩库、Gupy 平台为什么天然没有种子可种,以及如何用一个可选的 `discoverer` 接口让 prober 从平台自己的全局职位 feed 里枚举候选看板。读完后你会掌握 freehire 的

gitblog_00996的博客 163

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Claude 2 API生产级接入:从踩坑到稳定交付的七步工程化实践

大型语言模型API调用远非简单发送HTTP请求,其本质是面向不确定性的系统工程。以Claude 2为例,理解其token计数机制、上下文衰减特性、system prompt强干预逻辑及stream响应语义断裂等底层原理,是构建高可用AI服务的前提。这类模型API的技术价值在于支撑长文本处理、结构化输出与合规可控生成,广泛应用于智能客服、技术文档生成、法律合同分析等企业级场景。本文聚焦Claude 2 API在真实生产环境中的关键约束与工程对策,涵盖上下文管理、响应截断防护、会话状态持久化及灰度发布机制等核心

weixin_30387799的博客 330

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Mythos模型能力跃迁:从代码生成到零日漏洞挖掘的技术解析

大型语言模型正从通用文本生成迈向系统级安全能力突破,其核心体现为对真实软件栈的深度理解与自主攻防推理。这类模型不再局限于语法补全,而是基于测试时计算(test-time compute)构建攻击状态机、执行符号执行、推导协议状态转换,并在SWE-bench Pro、CyberGym等高保真基准中逼近人类专家水平。技术价值在于将AI从‘辅助工具’升级为‘可验证的自动化安全分析师’,支撑漏洞挖掘、红队模拟与供应链审计等关键场景。尤其当模型能发现CVE-2026–4747这类17年陈酿漏洞并绕过EDR/SIEM防

439

TypeScript构建LLM CLI工具的逆向分析与工程实践

CLI工具是将大语言模型能力嵌入开发流程的关键接口,其本质是基于标准API封装的命令行交互层。核心原理在于解耦模型调用与工作流逻辑,通过TypeScript实现可调试、可扩展的请求编排、上下文管理与错误熔断。技术价值体现在轻量集成、CI/CD友好及高度定制化,广泛应用于代码生成、源码理解、多模型网关适配等场景。本文以cl4r1t4s为范例,深入解析其三层物理架构、Anthropic模型路由机制、TS工程配置细节及跨模型(如DeepSeek)接入路径,揭示LLM CLI从设计、构建到排错的完整工程链路。

weixin_30542079的博客 338

.NET技术博客如何构建可验证的工程化知识体系

在.NET技术生态中,'技术博客'早已超越工具教程范畴,演变为承载系统性认知、支撑工程决策的关键知识资产。其核心价值在于将抽象框架原理转化为可证伪、可复现、可跨版本验证的技术判断力——这依赖于对底层机制(如内存池管理、表达式树变形、HTTP/3握手流程)的穿透式理解,并通过性能基线、多版本兼容测试、结构化日志等工程实践落地。尤其在企业级开发中,面对.NET 6/7/8混合环境、遗留系统约束与高可用要求,博客内容必须具备反模式显影能力与硬件协同视角(如CPU缓存行、GC代际行为),才能真正赋能从应届生到Tec

as2886089的博客 396

文件上传漏洞代码审计实战:从Upload-Labs靶场到安全开发实践

文件上传功能是Web应用中的常见模块,其安全实现涉及用户输入验证、文件类型校验和路径处理等多个层面。从技术原理上看,文件上传漏洞通常源于对客户端数据的过度信任,未在服务端实施有效的白名单校验和内容安全检查。这类漏洞的技术价值在于,一旦被利用,攻击者可直接上传恶意脚本,获取服务器控制权,危害极大。在应用场景上,无论是电商平台的图片上传,还是社交媒体的文件分享,都需要严格的安全防护机制。本文以经典的Upload-Labs靶场为例,深入剖析前端JavaScript校验绕过、后端代码审计方法,并结合文件上传漏洞的常

weixin_30617797的博客 351

Windows下Flask开发必须用虚拟环境的实操指南

虚拟环境是Python项目依赖隔离的核心机制,其原理在于为每个项目创建独立的解释器副本与包管理空间,从根本上避免全局site-packages冲突。在Windows平台,由于缺乏Unix类系统的用户级路径隔离能力,pip默认安装会污染系统Python环境,导致版本错乱、ImportError频发等典型问题。技术价值体现在环境可复现、协作零干扰、CI/CD稳定可靠;典型应用场景包括本地Flask调试、多版本Flask共存、团队协同开发及轻量部署。本文聚焦Windows原生CMD/PowerShell下的ven

weixin_34214500的博客 503

ThinkPHP 5.0.23 RCE漏洞剖析:从反序列化到远程代码执行

在Web应用安全领域,远程代码执行(RCE)漏洞因其可直接控制服务器而危害极大。其原理常源于用户输入未经充分验证便被用于敏感操作,例如动态函数调用或不安全的反序列化。这类漏洞的技术价值在于揭示了框架核心逻辑与外部输入边界模糊可能带来的系统性风险,常见于各类PHP、Java等Web框架中。具体到应用场景,攻击者可通过污染特定参数链式触发危险函数,最终在未授权情况下执行任意系统命令。本文以ThinkPHP 5.0.23版本为例,深入解析其如何通过`__construct`方法重写与`filter`属性污染,构造

weixin_33991418的博客 352

Mythos漏洞挖掘AI:可调度的自动化安全流水线

漏洞挖掘正从依赖人工经验的黑盒过程,转向基于大模型的可编程、可复现、可规模化执行的技术范式。其核心原理是将软件系统建模为跨模块符号图谱,并结合测试时计算(test-time compute)驱动深度回溯验证,从而实现对内存破坏、逻辑绕过等高危缺陷的精准定位。这种能力不仅重构了渗透测试的成本结构与响应时效,更催生出漏洞可调度性系数(MSC)、AI提示词架构师等全新技术价值维度和工程实践场景。Mythos作为首个在真实CVE复现与 exploit 自动生成中系统性超越顶尖白帽工程师的模型,标志着自动化安全已进入

tomfish 313
上一篇: Nginx源码分析 - HTTP模块篇 - TCP连接建立过程(21)
下一篇: Java深入 - jdk8特性
老码农zhuli
老码农zhuli 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄14年 2456粉丝 312原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值