【问题标题】:Create a subrequest in a subrequest completion callback and a request body filter在子请求完成回调和请求正文过滤器中创建子请求
【发布时间】:2021-03-26 14:57:22
【问题描述】:

参考NGINX Development guide - HTTP - Subrequests,我设法在NGX_HTTP_PRECONTENT_PHASE 处理程序中创建了带有NGX_HTTP_SUBREQUEST_BACKGROUND | NGX_HTTP_SUBREQUEST_IN_MEMORY 的第一个子请求,然后在前一个请求的完成回调中创建了带有NGX_HTTP_SUBREQUEST_IN_MEMORY 的第二个子请求(无需唤醒父(主要)请求)。在调用第二个(最后一个)子请求回调后,主请求将被 NGINX 唤醒(将再次调用ngx_http_precontent_handler_by_em)。

代码sn-p(部分省略):

// NGINX won't wake parent request (http wake parent request:)
#define NGX_HTTP_EM_SUBREQUEST_INTERMEDIATE NGX_HTTP_SUBREQUEST_BACKGROUND | NGX_HTTP_SUBREQUEST_IN_MEMORY

static ngx_int_t ngx_http_em_subrequest_evaluation_done(ngx_http_request_t *req
    , void *data, ngx_int_t rc) {
    if (NGX_OK != rc) {
        ngx_rloge("subrequest@%p, data@%p, rc=%i", req, data, rc);
        return rc;
    }
    // req->main->content_handler = ngx_http_em_send_deny_response; // GOOD
    // ngx_http_em_send_deny_response(req->parent); // BAD
    // return NGX_HTTP_FORBIDDEN; // BAD
    return NGX_OK;
}

static ngx_int_t ngx_http_em_subrequest_evaluation(ngx_http_request_t *req, ngx_http_em_ctx_t* ctx) {
    listener->handler = ngx_http_em_subrequest_file_done;
    listener->data = NULL;

    ngx_http_request_t *sr = NULL; // used to receive the new (sub-)request structure
    ngx_int_t rc = ngx_http_subrequest(req, &sr_uri, &args, &sr, listener, NGX_HTTP_EM_SUBREQUEST_INTERMEDIATE);

    return NGX_OK;
}

static ngx_int_t ngx_http_em_subrequest_token_done(ngx_http_request_t *req
    , void *data, ngx_int_t rc) {
    return ngx_http_em_subrequest_evaluation(req->parent, ctx);
}

static ngx_int_t ngx_http_em_subrequest_token(ngx_http_request_t *req, ngx_http_em_ctx_t* ctx) {
    listener->handler = ngx_http_em_subrequest_token_done;
    listener->data = NULL;

    ngx_http_request_t *sr = NULL; // used to receive the new (sub-)request structure
    ngx_int_t rc = ngx_http_subrequest(req, &sr_uri, &args, &sr, listener, NGX_HTTP_EM_SUBREQUEST_INTERMEDIATE);

    return NGX_OK;
}

ngx_int_t ngx_http_precontent_handler_by_em(ngx_http_request_t *req) {
    if (req->main != req) {
        logdf("req@%p vs req->main@%p, %.*s", req, req->main, ARGS_NGX_STR(req->uri));
        return NGX_DECLINED;
    }
    logdf("%.*s", ARGS_NGX_STR(req->uri));
    if (ctx) {
        if (ctx->hasAnyPending()) {
            return NGX_AGAIN;
        }
        if (0 == ctx->status) { 
            return NGX_OK; // NGX_HTTP_FORBIDDEN;
        }
        return NGX_DONE; // NGX_DECLINED;
    }
    // create ctx codes omitted for brevity ...
    ngx_int_t rc = ngx_http_em_subrequest_token(req, ctx);
    if (NGX_ERROR != rc) {
        return NGX_DONE;
    }
    return NGX_OK;
}

但是,如果我在我的请求正文过滤器中创建了一个子请求,则该请求会挂起,直到出现错误的网关错误。

static ngx_int_t ngx_http_em_subrequest_sp_list_done(ngx_http_request_t *req, void *data, ngx_int_t rc) {
    ngx_http_em_filter_request_body(req, ctx);
    ngx_http_next_request_body_filter(req, newchain);
    //req->parent->write_event_handler; // ngx_http_request_empty_handler
    return NGX_OK;
}

static ngx_int_t ngx_http_em_subrequest_sp_list(ngx_http_request_t *req, ngx_http_em_ctx_t* ctx) {
    listener->handler = ngx_http_em_subrequest_sp_list_fields_done;
    listener->data = NULL;
    ngx_int_t rc = ngx_http_subrequest(req, &sr_uri, &args, &sr, listener, NGX_HTTP_SUBREQUEST_IN_MEMORY);
    return NGX_OK;
}

ngx_int_t ngx_http_request_body_filter_by_em(ngx_http_request_t *req, ngx_chain_t *in) {
    // TODO Auto-generated method stub
    logdf(LOG_SUBREQ "%.*s", LOG_SUBREQ_ARG(req), ARGS_NGX_STR(req->uri));
    if (in) {
        auto ctx = ngx_http_em_get_module_ctx(req);
        if (ctx) {
            ngx_http_em_subrequest_sp_list(req, ctx);
            return NGX_OK;
        }
    }
    return ngx_http_next_request_body_filter(req, in);
}

有人知道怎么做吗?

typedef struct {
    ngx_http_post_subrequest_pt       handler;
    void                             *data;
} ngx_http_post_subrequest_t;

【问题讨论】:

  • C 或 C++,不能两者兼得。
  • 我的模块是用 C++ (NGINX by C) 编写的

标签: c++ c http nginx asynchronous


【解决方案1】:

现在,我跟踪分析了 NGINX 代码。

当断点设置在ngx_http_request_body_filter_by_em 时,其回调如下所示:

    ngx_http_em_module.dll!ngx_http_request_body_filter_by_em(ngx_http_request_s * req, ngx_chain_s * in) Line 2637 C++
    NGINX.exe!ngx_http_request_body_length_filter(ngx_http_request_s * r, ngx_chain_s * in) Line 987    C
    NGINX.exe!ngx_http_request_body_filter(ngx_http_request_s * r, ngx_chain_s * in) Line 923   C
    NGINX.exe!ngx_http_read_client_request_body(ngx_http_request_s * r, void(*)(ngx_http_request_s *) post_handler) Line 102    C
    NGINX.exe!ngx_http_proxy_handler(ngx_http_request_s * r) Line 937   C
    NGINX.exe!ngx_http_core_content_phase(ngx_http_request_s * r, ngx_http_phase_handler_s * ph) Line 1247  C
    NGINX.exe!ngx_http_core_run_phases(ngx_http_request_s * r) Line 868 C
>   NGINX.exe!ngx_http_run_posted_requests(ngx_connection_s * c) Line 2408  C
    NGINX.exe!ngx_http_upstream_handler(ngx_event_s * ev) Line 1287 C
    NGINX.exe!ngx_event_process_posted(ngx_cycle_s * cycle, ngx_queue_s * posted) Line 35   C
    NGINX.exe!ngx_process_events_and_timers(ngx_cycle_s * cycle) Line 265   C
    NGINX.exe!ngx_worker_thread(void * data) Line 795   C

NGINX 代码 sn-p:

ngx_int_t ngx_http_request_body_filter_by_em(ngx_http_request_t *req, ngx_chain_t *in) {
    // u->read_event_handler = 0x00007ff7b74cf3f0 {NGINX.exe!ngx_http_upstream_process_upstream(ngx_http_request_s *, ngx_http_upstream_s *)}
    //req->main->posted_requests    0x0000000000000000 <NULL>   ngx_http_posted_request_s *
    //req->connection   0x0000029cc2080270 {data=0x0000029cc03e2e60 read=0x0000029cc20b8210 {data=0x0000029cc2080270 write=0 ...} ...}  ngx_connection_s *
    //req->main->write_event_handler    0x00007ff7b742c3ec {NGINX.exe!ngx_http_request_empty_handler}   void(*)(ngx_http_request_s *)
}

static ngx_int_t ngx_http_proxy_handler(ngx_http_request_t *r) {
    //...
    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        return rc;
    }
    return NGX_DONE;
}

ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) {
    //...
    if (r->content_handler) {
        r->write_event_handler = ngx_http_request_empty_handler;
        ngx_http_finalize_request(r, r->content_handler(r));
        return NGX_OK;
    }
    //...
}

void ngx_http_core_run_phases(ngx_http_request_t *r) {
    //...
    while (ph[r->phase_handler].checker) {
        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
        if (rc == NGX_OK) {
            return;
        }
    }
}

void ngx_http_handler(ngx_http_request_t *r) {
    //...
    r->write_event_handler = ngx_http_core_run_phases;
    ngx_http_core_run_phases(r);
}

void ngx_http_run_posted_requests(ngx_connection_t *c) {
    // https://english.stackexchange.com/questions/23023/iterate-vs-iterate-over
    //... Iterates ((ngx_http_request_t*)(c->data))->main->posted_requests
    for ( ;; ) {
        r->write_event_handler(r);
    }
}

ngx_http_run_posted_requests,我发现r-&gt;write_event_handler可能是以下函数之一:

0x00007ff7b742baf0 {NGINX.exe!ngx_http_handler}
0x00007ff7b742c3ec {NGINX.exe!ngx_http_request_empty_handler}
0x00007ff7b74d42f0 {NGINX.exe!ngx_http_upstream_wr_check_broken_connection(ngx_http_request_s *)}
0x00007ff7b74cc5c0 {NGINX.exe!ngx_http_upstream_process_downstream(ngx_http_request_s *)}
0x00007ff7b742bf19 {NGINX.exe!ngx_http_core_run_phases} void(*)(ngx_http_request_s *)

结果发现ngx_http_upstream_initngx_http_em_subrequest_sp_list_done之后没有被调用。

此外,我还检查并记录了req-&gt;write_event_handlerreq-&gt;main-&gt;posted_requestsreq-&gt;main-&gt;write_event_handler 在调用这些ngx_http_post_subrequest_pt 处理程序时的值:

    req->write_event_handler    0x00007ff7b742bf19 {NGINX.exe!ngx_http_core_run_phases} void(*)(ngx_http_request_s *)
    req->main->posted_requests  0x0000000000000000 <NULL>   ngx_http_posted_request_s *
    req->main->write_event_handler  0x00007ff7b742bf19 {NGINX.exe!ngx_http_core_run_phases} void(*)(ngx_http_request_s *)

>   ngx_http_em_module.dll!ngx_http_em_subrequest_attributes_done(ngx_http_request_s * req, void * data, __int64 rc) Line 1684  C++
    NGINX.exe!ngx_http_finalize_request(ngx_http_request_s * r, __int64 rc) Line 2465   C
    NGINX.exe!ngx_http_core_rewrite_phase(ngx_http_request_s * r, ngx_http_phase_handler_s * ph) Line 937   C
    NGINX.exe!ngx_http_core_run_phases(ngx_http_request_s * r) Line 868 C
    NGINX.exe!ngx_http_handler(ngx_http_request_s * r) Line 852 C
    NGINX.exe!ngx_http_run_posted_requests(ngx_connection_s * c) Line 2408  C
    NGINX.exe!ngx_http_process_request_headers(ngx_event_s * rev) Line 1502 C
    NGINX.exe!ngx_http_process_request_line(ngx_event_s * rev) Line 1153    C
    NGINX.exe!ngx_http_wait_request_handler(ngx_event_s * rev) Line 501 C
    NGINX.exe!ngx_event_process_posted(ngx_cycle_s * cycle, ngx_queue_s * posted) Line 35   C
    NGINX.exe!ngx_process_events_and_timers(ngx_cycle_s * cycle) Line 265   C
    NGINX.exe!ngx_worker_thread(void * data) Line 795   C


    req->write_event_handler    0x00007ff7b74cc5c0 {NGINX.exe!ngx_http_upstream_process_downstream(ngx_http_request_s *)}   void(*)(ngx_http_request_s *)
    req->main->posted_requests  0x0000000000000000 <NULL>   ngx_http_posted_request_s *
    req->main->write_event_handler  0x00007ff7b742bf19 {NGINX.exe!ngx_http_core_run_phases} void(*)(ngx_http_request_s *)

>   ngx_http_em_module.dll!ngx_http_em_subrequest_token_done(ngx_http_request_s * req, void * data, __int64 rc) Line 1295   C++
    NGINX.exe!ngx_http_finalize_request(ngx_http_request_s * r, __int64 rc) Line 2465   C
    NGINX.exe!ngx_http_upstream_finalize_request(ngx_http_request_s * r, ngx_http_upstream_s * u, __int64 rc) Line 4495 C
    NGINX.exe!ngx_http_upstream_process_request(ngx_http_request_s * r, ngx_http_upstream_s * u) Line 4051  C
    NGINX.exe!ngx_http_upstream_process_upstream(ngx_http_request_s * r, ngx_http_upstream_s * u) Line 3963 C
    NGINX.exe!ngx_http_upstream_send_response(ngx_http_request_s * r, ngx_http_upstream_s * u) Line 3241    C
    NGINX.exe!ngx_http_upstream_process_header(ngx_http_request_s * r, ngx_http_upstream_s * u) Line 2435   C
    NGINX.exe!ngx_http_upstream_handler(ngx_event_s * ev) Line 1286 C
    NGINX.exe!ngx_event_process_posted(ngx_cycle_s * cycle, ngx_queue_s * posted) Line 35   C
    NGINX.exe!ngx_process_events_and_timers(ngx_cycle_s * cycle) Line 265   C
    NGINX.exe!ngx_worker_thread(void * data) Line 795   C


    req->write_event_handler    0x00007ff7b742bf19 {NGINX.exe!ngx_http_core_run_phases} void(*)(ngx_http_request_s *)
    req->main->posted_requests  0x0000000000000000 <NULL>   ngx_http_posted_request_s *
    req->main->write_event_handler  0x00007ff7b742bf19 {NGINX.exe!ngx_http_core_run_phases} void(*)(ngx_http_request_s *)

>   ngx_http_em_module.dll!ngx_http_em_subrequest_evaluation_done(ngx_http_request_s * req, void * data, __int64 rc) Line 1368  C++
    NGINX.exe!ngx_http_finalize_request(ngx_http_request_s * r, __int64 rc) Line 2465   C
    NGINX.exe!ngx_http_core_rewrite_phase(ngx_http_request_s * r, ngx_http_phase_handler_s * ph) Line 937   C
    NGINX.exe!ngx_http_core_run_phases(ngx_http_request_s * r) Line 868 C
    NGINX.exe!ngx_http_handler(ngx_http_request_s * r) Line 852 C
    NGINX.exe!ngx_http_run_posted_requests(ngx_connection_s * c) Line 2408  C
    NGINX.exe!ngx_http_upstream_handler(ngx_event_s * ev) Line 1287 C
    NGINX.exe!ngx_event_process_posted(ngx_cycle_s * cycle, ngx_queue_s * posted) Line 35   C
    NGINX.exe!ngx_process_events_and_timers(ngx_cycle_s * cycle) Line 265   C
    NGINX.exe!ngx_worker_thread(void * data) Line 795   C


    req->write_event_handler    0x00007ff7b742bf19 {NGINX.exe!ngx_http_core_run_phases} void(*)(ngx_http_request_s *)
    req->main->posted_requests  0x0000000000000000 <NULL>   ngx_http_posted_request_s *
    req->main->write_event_handler  0x00007ff7b74d42f0 {NGINX.exe!ngx_http_upstream_wr_check_broken_connection(ngx_http_request_s *)}   void(*)(ngx_http_request_s *)

>   ngx_http_em_module.dll!ngx_http_em_subrequest_sp_list_done(ngx_http_request_s * req, void * data, __int64 rc) Line 1896 C++
    NGINX.exe!ngx_http_finalize_request(ngx_http_request_s * r, __int64 rc) Line 2465   C
    NGINX.exe!ngx_http_core_rewrite_phase(ngx_http_request_s * r, ngx_http_phase_handler_s * ph) Line 937   C
    NGINX.exe!ngx_http_core_run_phases(ngx_http_request_s * r) Line 868 C
    NGINX.exe!ngx_http_handler(ngx_http_request_s * r) Line 852 C
    NGINX.exe!ngx_http_run_posted_requests(ngx_connection_s * c) Line 2408  C
    NGINX.exe!ngx_http_upstream_handler(ngx_event_s * ev) Line 1287 C
    NGINX.exe!ngx_event_process_posted(ngx_cycle_s * cycle, ngx_queue_s * posted) Line 35   C
    NGINX.exe!ngx_process_events_and_timers(ngx_cycle_s * cycle) Line 265   C
    NGINX.exe!ngx_worker_thread(void * data) Line 795   C

现在,在我添加ngx_http_em_subrequest_sp_list_done 后,curl 客户端可以收到响应。

        // assert(ngx_http_upstream_wr_check_broken_connection == req->parent->write_event_handler);
        // assert(ngx_http_request_empty_handler == req->parent->write_event_handler);
        req->parent->write_event_handler = ngx_http_core_run_phases;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-23
    • 1970-01-01
    • 2016-08-26
    • 2013-06-18
    • 2018-12-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多