xref: /nginx/src/http/ngx_http_write_filter_module.c (revision 7947:51a260276425)
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 
13 static ngx_int_t ngx_http_write_filter_init(ngx_conf_t *cf);
14 
15 
16 static ngx_http_module_t  ngx_http_write_filter_module_ctx = {
17     NULL,                                  /* preconfiguration */
18     ngx_http_write_filter_init,            /* postconfiguration */
19 
20     NULL,                                  /* create main configuration */
21     NULL,                                  /* init main configuration */
22 
23     NULL,                                  /* create server configuration */
24     NULL,                                  /* merge server configuration */
25 
26     NULL,                                  /* create location configuration */
27     NULL,                                  /* merge location configuration */
28 };
29 
30 
31 ngx_module_t  ngx_http_write_filter_module = {
32     NGX_MODULE_V1,
33     &ngx_http_write_filter_module_ctx,     /* module context */
34     NULL,                                  /* module directives */
35     NGX_HTTP_MODULE,                       /* module type */
36     NULL,                                  /* init master */
37     NULL,                                  /* init module */
38     NULL,                                  /* init process */
39     NULL,                                  /* init thread */
40     NULL,                                  /* exit thread */
41     NULL,                                  /* exit process */
42     NULL,                                  /* exit master */
43     NGX_MODULE_V1_PADDING
44 };
45 
46 
47 ngx_int_t
48 ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
49 {
50     off_t                      size, sent, nsent, limit;
51     ngx_uint_t                 last, flush, sync;
52     ngx_msec_t                 delay;
53     ngx_chain_t               *cl, *ln, **ll, *chain;
54     ngx_connection_t          *c;
55     ngx_http_core_loc_conf_t  *clcf;
56 
57     c = r->connection;
58 
59     if (c->error) {
60         return NGX_ERROR;
61     }
62 
63     size = 0;
64     flush = 0;
65     sync = 0;
66     last = 0;
67     ll = &r->out;
68 
69     /* find the size, the flush point and the last link of the saved chain */
70 
71     for (cl = r->out; cl; cl = cl->next) {
72         ll = &cl->next;
73 
74         ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
75                        "write old buf t:%d f:%d %p, pos %p, size: %z "
76                        "file: %O, size: %O",
77                        cl->buf->temporary, cl->buf->in_file,
78                        cl->buf->start, cl->buf->pos,
79                        cl->buf->last - cl->buf->pos,
80                        cl->buf->file_pos,
81                        cl->buf->file_last - cl->buf->file_pos);
82 
83         if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
84             ngx_log_error(NGX_LOG_ALERT, c->log, 0,
85                           "zero size buf in writer "
86                           "t:%d r:%d f:%d %p %p-%p %p %O-%O",
87                           cl->buf->temporary,
88                           cl->buf->recycled,
89                           cl->buf->in_file,
90                           cl->buf->start,
91                           cl->buf->pos,
92                           cl->buf->last,
93                           cl->buf->file,
94                           cl->buf->file_pos,
95                           cl->buf->file_last);
96 
97             ngx_debug_point();
98             return NGX_ERROR;
99         }
100 
101         if (ngx_buf_size(cl->buf) < 0) {
102             ngx_log_error(NGX_LOG_ALERT, c->log, 0,
103                           "negative size buf in writer "
104                           "t:%d r:%d f:%d %p %p-%p %p %O-%O",
105                           cl->buf->temporary,
106                           cl->buf->recycled,
107                           cl->buf->in_file,
108                           cl->buf->start,
109                           cl->buf->pos,
110                           cl->buf->last,
111                           cl->buf->file,
112                           cl->buf->file_pos,
113                           cl->buf->file_last);
114 
115             ngx_debug_point();
116             return NGX_ERROR;
117         }
118 
119         size += ngx_buf_size(cl->buf);
120 
121         if (cl->buf->flush || cl->buf->recycled) {
122             flush = 1;
123         }
124 
125         if (cl->buf->sync) {
126             sync = 1;
127         }
128 
129         if (cl->buf->last_buf) {
130             last = 1;
131         }
132     }
133 
134     /* add the new chain to the existent one */
135 
136     for (ln = in; ln; ln = ln->next) {
137         cl = ngx_alloc_chain_link(r->pool);
138         if (cl == NULL) {
139             return NGX_ERROR;
140         }
141 
142         cl->buf = ln->buf;
143         *ll = cl;
144         ll = &cl->next;
145 
146         ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
147                        "write new buf t:%d f:%d %p, pos %p, size: %z "
148                        "file: %O, size: %O",
149                        cl->buf->temporary, cl->buf->in_file,
150                        cl->buf->start, cl->buf->pos,
151                        cl->buf->last - cl->buf->pos,
152                        cl->buf->file_pos,
153                        cl->buf->file_last - cl->buf->file_pos);
154 
155         if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
156             ngx_log_error(NGX_LOG_ALERT, c->log, 0,
157                           "zero size buf in writer "
158                           "t:%d r:%d f:%d %p %p-%p %p %O-%O",
159                           cl->buf->temporary,
160                           cl->buf->recycled,
161                           cl->buf->in_file,
162                           cl->buf->start,
163                           cl->buf->pos,
164                           cl->buf->last,
165                           cl->buf->file,
166                           cl->buf->file_pos,
167                           cl->buf->file_last);
168 
169             ngx_debug_point();
170             return NGX_ERROR;
171         }
172 
173         if (ngx_buf_size(cl->buf) < 0) {
174             ngx_log_error(NGX_LOG_ALERT, c->log, 0,
175                           "negative size buf in writer "
176                           "t:%d r:%d f:%d %p %p-%p %p %O-%O",
177                           cl->buf->temporary,
178                           cl->buf->recycled,
179                           cl->buf->in_file,
180                           cl->buf->start,
181                           cl->buf->pos,
182                           cl->buf->last,
183                           cl->buf->file,
184                           cl->buf->file_pos,
185                           cl->buf->file_last);
186 
187             ngx_debug_point();
188             return NGX_ERROR;
189         }
190 
191         size += ngx_buf_size(cl->buf);
192 
193         if (cl->buf->flush || cl->buf->recycled) {
194             flush = 1;
195         }
196 
197         if (cl->buf->sync) {
198             sync = 1;
199         }
200 
201         if (cl->buf->last_buf) {
202             last = 1;
203         }
204     }
205 
206     *ll = NULL;
207 
208     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
209                    "http write filter: l:%ui f:%ui s:%O", last, flush, size);
210 
211     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
212 
213     /*
214      * avoid the output if there are no last buf, no flush point,
215      * there are the incoming bufs and the size of all bufs
216      * is smaller than "postpone_output" directive
217      */
218 
219     if (!last && !flush && in && size < (off_t) clcf->postpone_output) {
220         return NGX_OK;
221     }
222 
223     if (c->write->delayed) {
224         c->buffered |= NGX_HTTP_WRITE_BUFFERED;
225         return NGX_AGAIN;
226     }
227 
228     if (size == 0
229         && !(c->buffered & NGX_LOWLEVEL_BUFFERED)
230         && !(last && c->need_last_buf))
231     {
232         if (last || flush || sync) {
233             for (cl = r->out; cl; /* void */) {
234                 ln = cl;
235                 cl = cl->next;
236                 ngx_free_chain(r->pool, ln);
237             }
238 
239             r->out = NULL;
240             c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
241 
242             return NGX_OK;
243         }
244 
245         ngx_log_error(NGX_LOG_ALERT, c->log, 0,
246                       "the http output chain is empty");
247 
248         ngx_debug_point();
249 
250         return NGX_ERROR;
251     }
252 
253     if (!r->limit_rate_set) {
254         r->limit_rate = ngx_http_complex_value_size(r, clcf->limit_rate, 0);
255         r->limit_rate_set = 1;
256     }
257 
258     if (r->limit_rate) {
259 
260         if (!r->limit_rate_after_set) {
261             r->limit_rate_after = ngx_http_complex_value_size(r,
262                                                     clcf->limit_rate_after, 0);
263             r->limit_rate_after_set = 1;
264         }
265 
266         limit = (off_t) r->limit_rate * (ngx_time() - r->start_sec + 1)
267                 - (c->sent - r->limit_rate_after);
268 
269         if (limit <= 0) {
270             c->write->delayed = 1;
271             delay = (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1);
272             ngx_add_timer(c->write, delay);
273 
274             c->buffered |= NGX_HTTP_WRITE_BUFFERED;
275 
276             return NGX_AGAIN;
277         }
278 
279         if (clcf->sendfile_max_chunk
280             && (off_t) clcf->sendfile_max_chunk < limit)
281         {
282             limit = clcf->sendfile_max_chunk;
283         }
284 
285     } else {
286         limit = clcf->sendfile_max_chunk;
287     }
288 
289     sent = c->sent;
290 
291     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
292                    "http write filter limit %O", limit);
293 
294     chain = c->send_chain(c, r->out, limit);
295 
296     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
297                    "http write filter %p", chain);
298 
299     if (chain == NGX_CHAIN_ERROR) {
300         c->error = 1;
301         return NGX_ERROR;
302     }
303 
304     if (r->limit_rate) {
305 
306         nsent = c->sent;
307 
308         if (r->limit_rate_after) {
309 
310             sent -= r->limit_rate_after;
311             if (sent < 0) {
312                 sent = 0;
313             }
314 
315             nsent -= r->limit_rate_after;
316             if (nsent < 0) {
317                 nsent = 0;
318             }
319         }
320 
321         delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate);
322 
323         if (delay > 0) {
324             c->write->delayed = 1;
325             ngx_add_timer(c->write, delay);
326         }
327     }
328 
329     if (chain && c->write->ready && !c->write->delayed) {
330         ngx_post_event(c->write, &ngx_posted_next_events);
331     }
332 
333     for (cl = r->out; cl && cl != chain; /* void */) {
334         ln = cl;
335         cl = cl->next;
336         ngx_free_chain(r->pool, ln);
337     }
338 
339     r->out = chain;
340 
341     if (chain) {
342         c->buffered |= NGX_HTTP_WRITE_BUFFERED;
343         return NGX_AGAIN;
344     }
345 
346     c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
347 
348     if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) {
349         return NGX_AGAIN;
350     }
351 
352     return NGX_OK;
353 }
354 
355 
356 static ngx_int_t
357 ngx_http_write_filter_init(ngx_conf_t *cf)
358 {
359     ngx_http_top_body_filter = ngx_http_write_filter;
360 
361     return NGX_OK;
362 }
363