ngx_http_copy_filter_module.c revision e18a033b
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
13typedef struct {
14    ngx_bufs_t  bufs;
15} ngx_http_copy_filter_conf_t;
16
17
18#if (NGX_HAVE_FILE_AIO)
19static void ngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx,
20    ngx_file_t *file);
21static void ngx_http_copy_aio_event_handler(ngx_event_t *ev);
22#if (NGX_HAVE_AIO_SENDFILE)
23static ssize_t ngx_http_copy_aio_sendfile_preload(ngx_buf_t *file);
24static void ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev);
25#endif
26#endif
27#if (NGX_THREADS)
28static ngx_int_t ngx_http_copy_thread_handler(ngx_thread_task_t *task,
29    ngx_file_t *file);
30static void ngx_http_copy_thread_event_handler(ngx_event_t *ev);
31#endif
32
33static void *ngx_http_copy_filter_create_conf(ngx_conf_t *cf);
34static char *ngx_http_copy_filter_merge_conf(ngx_conf_t *cf,
35    void *parent, void *child);
36static ngx_int_t ngx_http_copy_filter_init(ngx_conf_t *cf);
37
38
39static ngx_command_t  ngx_http_copy_filter_commands[] = {
40
41    { ngx_string("output_buffers"),
42      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
43      ngx_conf_set_bufs_slot,
44      NGX_HTTP_LOC_CONF_OFFSET,
45      offsetof(ngx_http_copy_filter_conf_t, bufs),
46      NULL },
47
48      ngx_null_command
49};
50
51
52static ngx_http_module_t  ngx_http_copy_filter_module_ctx = {
53    NULL,                                  /* preconfiguration */
54    ngx_http_copy_filter_init,             /* postconfiguration */
55
56    NULL,                                  /* create main configuration */
57    NULL,                                  /* init main configuration */
58
59    NULL,                                  /* create server configuration */
60    NULL,                                  /* merge server configuration */
61
62    ngx_http_copy_filter_create_conf,      /* create location configuration */
63    ngx_http_copy_filter_merge_conf        /* merge location configuration */
64};
65
66
67ngx_module_t  ngx_http_copy_filter_module = {
68    NGX_MODULE_V1,
69    &ngx_http_copy_filter_module_ctx,      /* module context */
70    ngx_http_copy_filter_commands,         /* module directives */
71    NGX_HTTP_MODULE,                       /* module type */
72    NULL,                                  /* init master */
73    NULL,                                  /* init module */
74    NULL,                                  /* init process */
75    NULL,                                  /* init thread */
76    NULL,                                  /* exit thread */
77    NULL,                                  /* exit process */
78    NULL,                                  /* exit master */
79    NGX_MODULE_V1_PADDING
80};
81
82
83static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
84
85
86static ngx_int_t
87ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in)
88{
89    ngx_int_t                     rc;
90    ngx_connection_t             *c;
91    ngx_output_chain_ctx_t       *ctx;
92    ngx_http_core_loc_conf_t     *clcf;
93    ngx_http_copy_filter_conf_t  *conf;
94
95    c = r->connection;
96
97    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
98                   "http copy filter: \"%V?%V\"", &r->uri, &r->args);
99
100    ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module);
101
102    if (ctx == NULL) {
103        ctx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t));
104        if (ctx == NULL) {
105            return NGX_ERROR;
106        }
107
108        ngx_http_set_ctx(r, ctx, ngx_http_copy_filter_module);
109
110        conf = ngx_http_get_module_loc_conf(r, ngx_http_copy_filter_module);
111        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
112
113        ctx->sendfile = c->sendfile;
114        ctx->need_in_memory = r->main_filter_need_in_memory
115                              || r->filter_need_in_memory;
116        ctx->need_in_temp = r->filter_need_temporary;
117
118        ctx->alignment = clcf->directio_alignment;
119
120        ctx->pool = r->pool;
121        ctx->bufs = conf->bufs;
122        ctx->tag = (ngx_buf_tag_t) &ngx_http_copy_filter_module;
123
124        ctx->output_filter = (ngx_output_chain_filter_pt)
125                                  ngx_http_next_body_filter;
126        ctx->filter_ctx = r;
127
128#if (NGX_HAVE_FILE_AIO)
129        if (ngx_file_aio && clcf->aio == NGX_HTTP_AIO_ON) {
130            ctx->aio_handler = ngx_http_copy_aio_handler;
131#if (NGX_HAVE_AIO_SENDFILE)
132            ctx->aio_preload = ngx_http_copy_aio_sendfile_preload;
133#endif
134        }
135#endif
136
137#if (NGX_THREADS)
138        if (clcf->aio == NGX_HTTP_AIO_THREADS) {
139            ctx->thread_handler = ngx_http_copy_thread_handler;
140        }
141#endif
142
143        if (in && in->buf && ngx_buf_size(in->buf)) {
144            r->request_output = 1;
145        }
146    }
147
148#if (NGX_HAVE_FILE_AIO || NGX_THREADS)
149    ctx->aio = r->aio;
150#endif
151
152    rc = ngx_output_chain(ctx, in);
153
154    if (ctx->in == NULL) {
155        r->buffered &= ~NGX_HTTP_COPY_BUFFERED;
156
157    } else {
158        r->buffered |= NGX_HTTP_COPY_BUFFERED;
159    }
160
161    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
162                   "http copy filter: %i \"%V?%V\"", rc, &r->uri, &r->args);
163
164    return rc;
165}
166
167
168#if (NGX_HAVE_FILE_AIO)
169
170static void
171ngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx, ngx_file_t *file)
172{
173    ngx_http_request_t *r;
174
175    r = ctx->filter_ctx;
176
177    file->aio->data = r;
178    file->aio->handler = ngx_http_copy_aio_event_handler;
179
180    r->main->blocked++;
181    r->aio = 1;
182    ctx->aio = 1;
183}
184
185
186static void
187ngx_http_copy_aio_event_handler(ngx_event_t *ev)
188{
189    ngx_event_aio_t     *aio;
190    ngx_connection_t    *c;
191    ngx_http_request_t  *r;
192
193    aio = ev->data;
194    r = aio->data;
195    c = r->connection;
196
197    ngx_http_set_log_request(c->log, r);
198
199    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
200                   "http aio: \"%V?%V\"", &r->uri, &r->args);
201
202    r->main->blocked--;
203    r->aio = 0;
204
205    r->write_event_handler(r);
206
207    ngx_http_run_posted_requests(c);
208}
209
210
211#if (NGX_HAVE_AIO_SENDFILE)
212
213static ssize_t
214ngx_http_copy_aio_sendfile_preload(ngx_buf_t *file)
215{
216    ssize_t                  n;
217    static u_char            buf[1];
218    ngx_event_aio_t         *aio;
219    ngx_http_request_t      *r;
220    ngx_output_chain_ctx_t  *ctx;
221
222    n = ngx_file_aio_read(file->file, buf, 1, file->file_pos, NULL);
223
224    if (n == NGX_AGAIN) {
225        aio = file->file->aio;
226        aio->handler = ngx_http_copy_aio_sendfile_event_handler;
227
228        r = aio->data;
229        r->main->blocked++;
230        r->aio = 1;
231
232        ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module);
233        ctx->aio = 1;
234    }
235
236    return n;
237}
238
239
240static void
241ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev)
242{
243    ngx_event_aio_t     *aio;
244    ngx_http_request_t  *r;
245
246    aio = ev->data;
247    r = aio->data;
248
249    r->main->blocked--;
250    r->aio = 0;
251    ev->complete = 0;
252
253    r->connection->write->handler(r->connection->write);
254}
255
256#endif
257#endif
258
259
260#if (NGX_THREADS)
261
262static ngx_int_t
263ngx_http_copy_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
264{
265    ngx_str_t                  name;
266    ngx_thread_pool_t         *tp;
267    ngx_http_request_t        *r;
268    ngx_output_chain_ctx_t    *ctx;
269    ngx_http_core_loc_conf_t  *clcf;
270
271    r = file->thread_ctx;
272
273    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
274    tp = clcf->thread_pool;
275
276    if (tp == NULL) {
277        if (ngx_http_complex_value(r, clcf->thread_pool_value, &name)
278            != NGX_OK)
279        {
280            return NGX_ERROR;
281        }
282
283        tp = ngx_thread_pool_get((ngx_cycle_t *) ngx_cycle, &name);
284
285        if (tp == NULL) {
286            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
287                          "thread pool \"%V\" not found", &name);
288            return NGX_ERROR;
289        }
290    }
291
292    task->event.data = r;
293    task->event.handler = ngx_http_copy_thread_event_handler;
294
295    if (ngx_thread_task_post(tp, task) != NGX_OK) {
296        return NGX_ERROR;
297    }
298
299    r->main->blocked++;
300    r->aio = 1;
301
302    ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module);
303    ctx->aio = 1;
304
305    return NGX_OK;
306}
307
308
309static void
310ngx_http_copy_thread_event_handler(ngx_event_t *ev)
311{
312    ngx_connection_t    *c;
313    ngx_http_request_t  *r;
314
315    r = ev->data;
316    c = r->connection;
317
318    ngx_http_set_log_request(c->log, r);
319
320    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
321                   "http thread: \"%V?%V\"", &r->uri, &r->args);
322
323    r->main->blocked--;
324    r->aio = 0;
325
326    if (r->done) {
327        /*
328         * trigger connection event handler if the subrequest was
329         * already finalized; this can happen if the handler is used
330         * for sendfile() in threads
331         */
332
333        c->write->handler(c->write);
334
335    } else {
336        r->write_event_handler(r);
337        ngx_http_run_posted_requests(c);
338    }
339}
340
341#endif
342
343
344static void *
345ngx_http_copy_filter_create_conf(ngx_conf_t *cf)
346{
347    ngx_http_copy_filter_conf_t *conf;
348
349    conf = ngx_palloc(cf->pool, sizeof(ngx_http_copy_filter_conf_t));
350    if (conf == NULL) {
351        return NULL;
352    }
353
354    conf->bufs.num = 0;
355
356    return conf;
357}
358
359
360static char *
361ngx_http_copy_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
362{
363    ngx_http_copy_filter_conf_t *prev = parent;
364    ngx_http_copy_filter_conf_t *conf = child;
365
366    ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 2, 32768);
367
368    return NULL;
369}
370
371
372static ngx_int_t
373ngx_http_copy_filter_init(ngx_conf_t *cf)
374{
375    ngx_http_next_body_filter = ngx_http_top_body_filter;
376    ngx_http_top_body_filter = ngx_http_copy_filter;
377
378    return NGX_OK;
379}
380
381