upstream nginx-0.7.31
[nginx.git] / nginx / src / http / ngx_http_write_filter_module.c
1
2 /*
3  * Copyright (C) Igor Sysoev
4  */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10
11
12 static ngx_int_t ngx_http_write_filter_init(ngx_conf_t *cf);
13
14
15 static ngx_http_module_t  ngx_http_write_filter_module_ctx = {
16     NULL,                                  /* preconfiguration */
17     ngx_http_write_filter_init,            /* postconfiguration */
18
19     NULL,                                  /* create main configuration */
20     NULL,                                  /* init main configuration */
21
22     NULL,                                  /* create server configuration */
23     NULL,                                  /* merge server configuration */
24
25     NULL,                                  /* create location configuration */
26     NULL,                                  /* merge location configuration */
27 };
28
29
30 ngx_module_t  ngx_http_write_filter_module = {
31     NGX_MODULE_V1,
32     &ngx_http_write_filter_module_ctx,     /* module context */
33     NULL,                                  /* module directives */
34     NGX_HTTP_MODULE,                       /* module type */
35     NULL,                                  /* init master */
36     NULL,                                  /* init module */
37     NULL,                                  /* init process */
38     NULL,                                  /* init thread */
39     NULL,                                  /* exit thread */
40     NULL,                                  /* exit process */
41     NULL,                                  /* exit master */
42     NGX_MODULE_V1_PADDING
43 };
44
45
46 ngx_int_t
47 ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
48 {
49     off_t                      size, sent, limit;
50     ngx_uint_t                 last, flush;
51     ngx_msec_t                 delay;
52     ngx_chain_t               *cl, *ln, **ll, *chain;
53     ngx_connection_t          *c;
54     ngx_http_core_loc_conf_t  *clcf;
55
56     c = r->connection;
57
58     if (c->error) {
59         return NGX_ERROR;
60     }
61
62     size = 0;
63     flush = 0;
64     last = 0;
65     ll = &r->out;
66
67     /* find the size, the flush point and the last link of the saved chain */
68
69     for (cl = r->out; cl; cl = cl->next) {
70         ll = &cl->next;
71
72         ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
73                        "write old buf t:%d f:%d %p, pos %p, size: %z "
74                        "file: %O, size: %z",
75                        cl->buf->temporary, cl->buf->in_file,
76                        cl->buf->start, cl->buf->pos,
77                        cl->buf->last - cl->buf->pos,
78                        cl->buf->file_pos,
79                        cl->buf->file_last - cl->buf->file_pos);
80
81 #if 1
82         if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
83             ngx_log_error(NGX_LOG_ALERT, c->log, 0,
84                           "zero size buf in writer "
85                           "t:%d r:%d f:%d %p %p-%p %p %O-%O",
86                           cl->buf->temporary,
87                           cl->buf->recycled,
88                           cl->buf->in_file,
89                           cl->buf->start,
90                           cl->buf->pos,
91                           cl->buf->last,
92                           cl->buf->file,
93                           cl->buf->file_pos,
94                           cl->buf->file_last);
95
96             ngx_debug_point();
97             return NGX_ERROR;
98         }
99 #endif
100
101         size += ngx_buf_size(cl->buf);
102
103         if (cl->buf->flush || cl->buf->recycled) {
104             flush = 1;
105         }
106
107         if (cl->buf->last_buf) {
108             last = 1;
109         }
110     }
111
112     /* add the new chain to the existent one */
113
114     for (ln = in; ln; ln = ln->next) {
115         cl = ngx_alloc_chain_link(r->pool);
116         if (cl == NULL) {
117             return NGX_ERROR;
118         }
119
120         cl->buf = ln->buf;
121         *ll = cl;
122         ll = &cl->next;
123
124         ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
125                        "write new buf t:%d f:%d %p, pos %p, size: %z "
126                        "file: %O, size: %z",
127                        cl->buf->temporary, cl->buf->in_file,
128                        cl->buf->start, cl->buf->pos,
129                        cl->buf->last - cl->buf->pos,
130                        cl->buf->file_pos,
131                        cl->buf->file_last - cl->buf->file_pos);
132
133 #if 1
134         if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
135             ngx_log_error(NGX_LOG_ALERT, c->log, 0,
136                           "zero size buf in writer "
137                           "t:%d r:%d f:%d %p %p-%p %p %O-%O",
138                           cl->buf->temporary,
139                           cl->buf->recycled,
140                           cl->buf->in_file,
141                           cl->buf->start,
142                           cl->buf->pos,
143                           cl->buf->last,
144                           cl->buf->file,
145                           cl->buf->file_pos,
146                           cl->buf->file_last);
147
148             ngx_debug_point();
149             return NGX_ERROR;
150         }
151 #endif
152
153         size += ngx_buf_size(cl->buf);
154
155         if (cl->buf->flush || cl->buf->recycled) {
156             flush = 1;
157         }
158
159         if (cl->buf->last_buf) {
160             last = 1;
161         }
162     }
163
164     *ll = NULL;
165
166     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
167                    "http write filter: l:%d f:%d s:%O", last, flush, size);
168
169     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
170
171     /*
172      * avoid the output if there are no last buf, no flush point,
173      * there are the incoming bufs and the size of all bufs
174      * is smaller than "postpone_output" directive
175      */
176
177     if (!last && !flush && in && size < (off_t) clcf->postpone_output) {
178         return NGX_OK;
179     }
180
181     if (c->write->delayed) {
182         c->buffered |= NGX_HTTP_WRITE_BUFFERED;
183         return NGX_AGAIN;
184     }
185
186     if (size == 0 && !(c->buffered & NGX_LOWLEVEL_BUFFERED)) {
187         if (last) {
188             r->out = NULL;
189             c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
190
191             return NGX_OK;
192         }
193
194         if (flush) {
195             do {
196                 r->out = r->out->next;
197             } while (r->out);
198
199             c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
200
201             return NGX_OK;
202         }
203
204         ngx_log_error(NGX_LOG_ALERT, c->log, 0,
205                       "the http output chain is empty");
206
207         ngx_debug_point();
208
209         return NGX_ERROR;
210     }
211
212     if (r->limit_rate) {
213         limit = r->limit_rate * (ngx_time() - r->start_sec + 1) - c->sent;
214
215         if (limit <= 0) {
216             c->write->delayed = 1;
217             ngx_add_timer(c->write,
218                           (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1));
219
220             c->buffered |= NGX_HTTP_WRITE_BUFFERED;
221
222             return NGX_AGAIN;
223         }
224
225     } else if (clcf->sendfile_max_chunk) {
226         limit = clcf->sendfile_max_chunk;
227
228     } else {
229         limit = 0;
230     }
231
232     sent = c->sent;
233
234     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
235                    "http write filter limit %O", limit);
236
237     chain = c->send_chain(c, r->out, limit);
238
239     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
240                    "http write filter %p", chain);
241
242     if (chain == NGX_CHAIN_ERROR) {
243         c->error = 1;
244         return NGX_ERROR;
245     }
246
247     if (r->limit_rate) {
248         delay = (ngx_msec_t) ((c->sent - sent) * 1000 / r->limit_rate + 1);
249
250         if (delay > 0) {
251             c->write->delayed = 1;
252             ngx_add_timer(c->write, delay);
253         }
254
255     } else if (c->write->ready
256                && clcf->sendfile_max_chunk
257                && (size_t) (c->sent - sent)
258                       >= clcf->sendfile_max_chunk - 2 * ngx_pagesize)
259     {
260         c->write->delayed = 1;
261         ngx_add_timer(c->write, 1);
262     }
263
264     for (cl = r->out; cl && cl != chain; /* void */) {
265         ln = cl;
266         cl = cl->next;
267         ngx_free_chain(r->pool, ln);
268     }
269
270     r->out = chain;
271
272     if (chain) {
273         c->buffered |= NGX_HTTP_WRITE_BUFFERED;
274         return NGX_AGAIN;
275     }
276
277     c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
278
279     if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) {
280         return NGX_AGAIN;
281     }
282
283     return NGX_OK;
284 }
285
286
287 static ngx_int_t
288 ngx_http_write_filter_init(ngx_conf_t *cf)
289 {
290     ngx_http_top_body_filter = ngx_http_write_filter;
291
292     return NGX_OK;
293 }