| |
| /* |
| * Copyright (C) Igor Sysoev |
| * Copyright (C) Nginx, Inc. |
| */ |
| |
| |
| #include <ngx_config.h> |
| #include <ngx_core.h> |
| #include <ngx_http.h> |
| |
| |
| static ngx_int_t ngx_http_postpone_filter_add(ngx_http_request_t *r, |
| ngx_chain_t *in); |
| static ngx_int_t ngx_http_postpone_filter_in_memory(ngx_http_request_t *r, |
| ngx_chain_t *in); |
| static ngx_int_t ngx_http_postpone_filter_init(ngx_conf_t *cf); |
| |
| |
| static ngx_http_module_t ngx_http_postpone_filter_module_ctx = { |
| NULL, /* preconfiguration */ |
| ngx_http_postpone_filter_init, /* postconfiguration */ |
| |
| NULL, /* create main configuration */ |
| NULL, /* init main configuration */ |
| |
| NULL, /* create server configuration */ |
| NULL, /* merge server configuration */ |
| |
| NULL, /* create location configuration */ |
| NULL /* merge location configuration */ |
| }; |
| |
| |
| ngx_module_t ngx_http_postpone_filter_module = { |
| NGX_MODULE_V1, |
| &ngx_http_postpone_filter_module_ctx, /* module context */ |
| NULL, /* module directives */ |
| NGX_HTTP_MODULE, /* module type */ |
| NULL, /* init master */ |
| NULL, /* init module */ |
| NULL, /* init process */ |
| NULL, /* init thread */ |
| NULL, /* exit thread */ |
| NULL, /* exit process */ |
| NULL, /* exit master */ |
| NGX_MODULE_V1_PADDING |
| }; |
| |
| |
| static ngx_http_output_body_filter_pt ngx_http_next_body_filter; |
| |
| |
| static ngx_int_t |
| ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in) |
| { |
| ngx_connection_t *c; |
| ngx_http_postponed_request_t *pr; |
| |
| c = r->connection; |
| |
| ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, |
| "http postpone filter \"%V?%V\" %p", &r->uri, &r->args, in); |
| |
| if (r->subrequest_in_memory) { |
| return ngx_http_postpone_filter_in_memory(r, in); |
| } |
| |
| if (r != c->data) { |
| |
| if (in) { |
| if (ngx_http_postpone_filter_add(r, in) != NGX_OK) { |
| return NGX_ERROR; |
| } |
| |
| return NGX_OK; |
| } |
| |
| #if 0 |
| /* TODO: SSI may pass NULL */ |
| ngx_log_error(NGX_LOG_ALERT, c->log, 0, |
| "http postpone filter NULL inactive request"); |
| #endif |
| |
| return NGX_OK; |
| } |
| |
| if (r->postponed == NULL) { |
| |
| if (in || c->buffered) { |
| return ngx_http_next_body_filter(r->main, in); |
| } |
| |
| return NGX_OK; |
| } |
| |
| if (in) { |
| if (ngx_http_postpone_filter_add(r, in) != NGX_OK) { |
| return NGX_ERROR; |
| } |
| } |
| |
| do { |
| pr = r->postponed; |
| |
| if (pr->request) { |
| |
| ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, |
| "http postpone filter wake \"%V?%V\"", |
| &pr->request->uri, &pr->request->args); |
| |
| r->postponed = pr->next; |
| |
| c->data = pr->request; |
| |
| return ngx_http_post_request(pr->request, NULL); |
| } |
| |
| if (pr->out == NULL) { |
| ngx_log_error(NGX_LOG_ALERT, c->log, 0, |
| "http postpone filter NULL output"); |
| |
| } else { |
| ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, |
| "http postpone filter output \"%V?%V\"", |
| &r->uri, &r->args); |
| |
| if (ngx_http_next_body_filter(r->main, pr->out) == NGX_ERROR) { |
| return NGX_ERROR; |
| } |
| } |
| |
| r->postponed = pr->next; |
| |
| } while (r->postponed); |
| |
| return NGX_OK; |
| } |
| |
| |
| static ngx_int_t |
| ngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in) |
| { |
| ngx_http_postponed_request_t *pr, **ppr; |
| |
| if (r->postponed) { |
| for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ } |
| |
| if (pr->request == NULL) { |
| goto found; |
| } |
| |
| ppr = &pr->next; |
| |
| } else { |
| ppr = &r->postponed; |
| } |
| |
| pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t)); |
| if (pr == NULL) { |
| return NGX_ERROR; |
| } |
| |
| *ppr = pr; |
| |
| pr->request = NULL; |
| pr->out = NULL; |
| pr->next = NULL; |
| |
| found: |
| |
| if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK) { |
| return NGX_OK; |
| } |
| |
| return NGX_ERROR; |
| } |
| |
| |
| static ngx_int_t |
| ngx_http_postpone_filter_in_memory(ngx_http_request_t *r, ngx_chain_t *in) |
| { |
| size_t len; |
| ngx_buf_t *b; |
| ngx_connection_t *c; |
| ngx_http_core_loc_conf_t *clcf; |
| |
| c = r->connection; |
| |
| ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, |
| "http postpone filter in memory"); |
| |
| if (r->out == NULL) { |
| clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
| |
| if (r->headers_out.content_length_n != -1) { |
| len = r->headers_out.content_length_n; |
| |
| if (len > clcf->subrequest_output_buffer_size) { |
| ngx_log_error(NGX_LOG_ERR, c->log, 0, |
| "too big subrequest response: %uz", len); |
| return NGX_ERROR; |
| } |
| |
| } else { |
| len = clcf->subrequest_output_buffer_size; |
| } |
| |
| b = ngx_create_temp_buf(r->pool, len); |
| if (b == NULL) { |
| return NGX_ERROR; |
| } |
| |
| b->last_buf = 1; |
| |
| r->out = ngx_alloc_chain_link(r->pool); |
| if (r->out == NULL) { |
| return NGX_ERROR; |
| } |
| |
| r->out->buf = b; |
| r->out->next = NULL; |
| } |
| |
| b = r->out->buf; |
| |
| for ( /* void */ ; in; in = in->next) { |
| |
| if (ngx_buf_special(in->buf)) { |
| continue; |
| } |
| |
| len = in->buf->last - in->buf->pos; |
| |
| if (len > (size_t) (b->end - b->last)) { |
| ngx_log_error(NGX_LOG_ERR, c->log, 0, |
| "too big subrequest response"); |
| return NGX_ERROR; |
| } |
| |
| ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, |
| "http postpone filter in memory %uz bytes", len); |
| |
| b->last = ngx_cpymem(b->last, in->buf->pos, len); |
| in->buf->pos = in->buf->last; |
| } |
| |
| return NGX_OK; |
| } |
| |
| |
| static ngx_int_t |
| ngx_http_postpone_filter_init(ngx_conf_t *cf) |
| { |
| ngx_http_next_body_filter = ngx_http_top_body_filter; |
| ngx_http_top_body_filter = ngx_http_postpone_filter; |
| |
| return NGX_OK; |
| } |