1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_http.h>
11
12
13static ngx_int_t ngx_http_postpone_filter_add(ngx_http_request_t *r,
14    ngx_chain_t *in);
15static ngx_int_t ngx_http_postpone_filter_init(ngx_conf_t *cf);
16
17
18static ngx_http_module_t  ngx_http_postpone_filter_module_ctx = {
19    NULL,                                  /* preconfiguration */
20    ngx_http_postpone_filter_init,         /* postconfiguration */
21
22    NULL,                                  /* create main configuration */
23    NULL,                                  /* init main configuration */
24
25    NULL,                                  /* create server configuration */
26    NULL,                                  /* merge server configuration */
27
28    NULL,                                  /* create location configuration */
29    NULL                                   /* merge location configuration */
30};
31
32
33ngx_module_t  ngx_http_postpone_filter_module = {
34    NGX_MODULE_V1,
35    &ngx_http_postpone_filter_module_ctx,  /* module context */
36    NULL,                                  /* module directives */
37    NGX_HTTP_MODULE,                       /* module type */
38    NULL,                                  /* init master */
39    NULL,                                  /* init module */
40    NULL,                                  /* init process */
41    NULL,                                  /* init thread */
42    NULL,                                  /* exit thread */
43    NULL,                                  /* exit process */
44    NULL,                                  /* exit master */
45    NGX_MODULE_V1_PADDING
46};
47
48
49static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
50
51
52static ngx_int_t
53ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in)
54{
55    ngx_connection_t              *c;
56    ngx_http_postponed_request_t  *pr;
57
58    c = r->connection;
59
60    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
61                   "http postpone filter \"%V?%V\" %p", &r->uri, &r->args, in);
62
63    if (r != c->data) {
64
65        if (in) {
66            ngx_http_postpone_filter_add(r, in);
67            return NGX_OK;
68        }
69
70#if 0
71        /* TODO: SSI may pass NULL */
72        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
73                      "http postpone filter NULL inactive request");
74#endif
75
76        return NGX_OK;
77    }
78
79    if (r->postponed == NULL) {
80
81        if (in || c->buffered) {
82            return ngx_http_next_body_filter(r->main, in);
83        }
84
85        return NGX_OK;
86    }
87
88    if (in) {
89        ngx_http_postpone_filter_add(r, in);
90    }
91
92    do {
93        pr = r->postponed;
94
95        if (pr->request) {
96
97            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
98                           "http postpone filter wake \"%V?%V\"",
99                           &pr->request->uri, &pr->request->args);
100
101            r->postponed = pr->next;
102
103            c->data = pr->request;
104
105            return ngx_http_post_request(pr->request, NULL);
106        }
107
108        if (pr->out == NULL) {
109            ngx_log_error(NGX_LOG_ALERT, c->log, 0,
110                          "http postpone filter NULL output");
111
112        } else {
113            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
114                           "http postpone filter output \"%V?%V\"",
115                           &r->uri, &r->args);
116
117            if (ngx_http_next_body_filter(r->main, pr->out) == NGX_ERROR) {
118                return NGX_ERROR;
119            }
120        }
121
122        r->postponed = pr->next;
123
124    } while (r->postponed);
125
126    return NGX_OK;
127}
128
129
130static ngx_int_t
131ngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in)
132{
133    ngx_http_postponed_request_t  *pr, **ppr;
134
135    if (r->postponed) {
136        for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ }
137
138        if (pr->request == NULL) {
139            goto found;
140        }
141
142        ppr = &pr->next;
143
144    } else {
145        ppr = &r->postponed;
146    }
147
148    pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
149    if (pr == NULL) {
150        return NGX_ERROR;
151    }
152
153    *ppr = pr;
154
155    pr->request = NULL;
156    pr->out = NULL;
157    pr->next = NULL;
158
159found:
160
161    if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK) {
162        return NGX_OK;
163    }
164
165    return NGX_ERROR;
166}
167
168
169static ngx_int_t
170ngx_http_postpone_filter_init(ngx_conf_t *cf)
171{
172    ngx_http_next_body_filter = ngx_http_top_body_filter;
173    ngx_http_top_body_filter = ngx_http_postpone_filter;
174
175    return NGX_OK;
176}
177