Annotation of embedaddon/nginx/src/http/modules/ngx_http_image_filter_module.c, revision 1.1.1.1
1.1 misho 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: #include <gd.h>
13:
14:
15: #define NGX_HTTP_IMAGE_OFF 0
16: #define NGX_HTTP_IMAGE_TEST 1
17: #define NGX_HTTP_IMAGE_SIZE 2
18: #define NGX_HTTP_IMAGE_RESIZE 3
19: #define NGX_HTTP_IMAGE_CROP 4
20: #define NGX_HTTP_IMAGE_ROTATE 5
21:
22:
23: #define NGX_HTTP_IMAGE_START 0
24: #define NGX_HTTP_IMAGE_READ 1
25: #define NGX_HTTP_IMAGE_PROCESS 2
26: #define NGX_HTTP_IMAGE_PASS 3
27: #define NGX_HTTP_IMAGE_DONE 4
28:
29:
30: #define NGX_HTTP_IMAGE_NONE 0
31: #define NGX_HTTP_IMAGE_JPEG 1
32: #define NGX_HTTP_IMAGE_GIF 2
33: #define NGX_HTTP_IMAGE_PNG 3
34:
35:
36: #define NGX_HTTP_IMAGE_BUFFERED 0x08
37:
38:
39: typedef struct {
40: ngx_uint_t filter;
41: ngx_uint_t width;
42: ngx_uint_t height;
43: ngx_uint_t angle;
44: ngx_uint_t jpeg_quality;
45: ngx_uint_t sharpen;
46:
47: ngx_flag_t transparency;
48: ngx_flag_t interlace;
49:
50: ngx_http_complex_value_t *wcv;
51: ngx_http_complex_value_t *hcv;
52: ngx_http_complex_value_t *acv;
53: ngx_http_complex_value_t *jqcv;
54: ngx_http_complex_value_t *shcv;
55:
56: size_t buffer_size;
57: } ngx_http_image_filter_conf_t;
58:
59:
60: typedef struct {
61: u_char *image;
62: u_char *last;
63:
64: size_t length;
65:
66: ngx_uint_t width;
67: ngx_uint_t height;
68: ngx_uint_t max_width;
69: ngx_uint_t max_height;
70: ngx_uint_t angle;
71:
72: ngx_uint_t phase;
73: ngx_uint_t type;
74: ngx_uint_t force;
75: } ngx_http_image_filter_ctx_t;
76:
77:
78: static ngx_int_t ngx_http_image_send(ngx_http_request_t *r,
79: ngx_http_image_filter_ctx_t *ctx, ngx_chain_t *in);
80: static ngx_uint_t ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in);
81: static ngx_int_t ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in);
82: static ngx_buf_t *ngx_http_image_process(ngx_http_request_t *r);
83: static ngx_buf_t *ngx_http_image_json(ngx_http_request_t *r,
84: ngx_http_image_filter_ctx_t *ctx);
85: static ngx_buf_t *ngx_http_image_asis(ngx_http_request_t *r,
86: ngx_http_image_filter_ctx_t *ctx);
87: static void ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b);
88: static ngx_int_t ngx_http_image_size(ngx_http_request_t *r,
89: ngx_http_image_filter_ctx_t *ctx);
90:
91: static ngx_buf_t *ngx_http_image_resize(ngx_http_request_t *r,
92: ngx_http_image_filter_ctx_t *ctx);
93: static gdImagePtr ngx_http_image_source(ngx_http_request_t *r,
94: ngx_http_image_filter_ctx_t *ctx);
95: static gdImagePtr ngx_http_image_new(ngx_http_request_t *r, int w, int h,
96: int colors);
97: static u_char *ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type,
98: gdImagePtr img, int *size);
99: static void ngx_http_image_cleanup(void *data);
100: static ngx_uint_t ngx_http_image_filter_get_value(ngx_http_request_t *r,
101: ngx_http_complex_value_t *cv, ngx_uint_t v);
102: static ngx_uint_t ngx_http_image_filter_value(ngx_str_t *value);
103:
104:
105: static void *ngx_http_image_filter_create_conf(ngx_conf_t *cf);
106: static char *ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent,
107: void *child);
108: static char *ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd,
109: void *conf);
110: static char *ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf,
111: ngx_command_t *cmd, void *conf);
112: static char *ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,
113: void *conf);
114: static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf);
115:
116:
117: static ngx_command_t ngx_http_image_filter_commands[] = {
118:
119: { ngx_string("image_filter"),
120: NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
121: ngx_http_image_filter,
122: NGX_HTTP_LOC_CONF_OFFSET,
123: 0,
124: NULL },
125:
126: { ngx_string("image_filter_jpeg_quality"),
127: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
128: ngx_http_image_filter_jpeg_quality,
129: NGX_HTTP_LOC_CONF_OFFSET,
130: 0,
131: NULL },
132:
133: { ngx_string("image_filter_sharpen"),
134: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
135: ngx_http_image_filter_sharpen,
136: NGX_HTTP_LOC_CONF_OFFSET,
137: 0,
138: NULL },
139:
140: { ngx_string("image_filter_transparency"),
141: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
142: ngx_conf_set_flag_slot,
143: NGX_HTTP_LOC_CONF_OFFSET,
144: offsetof(ngx_http_image_filter_conf_t, transparency),
145: NULL },
146:
147: { ngx_string("image_filter_interlace"),
148: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
149: ngx_conf_set_flag_slot,
150: NGX_HTTP_LOC_CONF_OFFSET,
151: offsetof(ngx_http_image_filter_conf_t, interlace),
152: NULL },
153:
154: { ngx_string("image_filter_buffer"),
155: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
156: ngx_conf_set_size_slot,
157: NGX_HTTP_LOC_CONF_OFFSET,
158: offsetof(ngx_http_image_filter_conf_t, buffer_size),
159: NULL },
160:
161: ngx_null_command
162: };
163:
164:
165: static ngx_http_module_t ngx_http_image_filter_module_ctx = {
166: NULL, /* preconfiguration */
167: ngx_http_image_filter_init, /* postconfiguration */
168:
169: NULL, /* create main configuration */
170: NULL, /* init main configuration */
171:
172: NULL, /* create server configuration */
173: NULL, /* merge server configuration */
174:
175: ngx_http_image_filter_create_conf, /* create location configuration */
176: ngx_http_image_filter_merge_conf /* merge location configuration */
177: };
178:
179:
180: ngx_module_t ngx_http_image_filter_module = {
181: NGX_MODULE_V1,
182: &ngx_http_image_filter_module_ctx, /* module context */
183: ngx_http_image_filter_commands, /* module directives */
184: NGX_HTTP_MODULE, /* module type */
185: NULL, /* init master */
186: NULL, /* init module */
187: NULL, /* init process */
188: NULL, /* init thread */
189: NULL, /* exit thread */
190: NULL, /* exit process */
191: NULL, /* exit master */
192: NGX_MODULE_V1_PADDING
193: };
194:
195:
196: static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
197: static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
198:
199:
200: static ngx_str_t ngx_http_image_types[] = {
201: ngx_string("image/jpeg"),
202: ngx_string("image/gif"),
203: ngx_string("image/png")
204: };
205:
206:
207: static ngx_int_t
208: ngx_http_image_header_filter(ngx_http_request_t *r)
209: {
210: off_t len;
211: ngx_http_image_filter_ctx_t *ctx;
212: ngx_http_image_filter_conf_t *conf;
213:
214: if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
215: return ngx_http_next_header_filter(r);
216: }
217:
218: ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
219:
220: if (ctx) {
221: ngx_http_set_ctx(r, NULL, ngx_http_image_filter_module);
222: return ngx_http_next_header_filter(r);
223: }
224:
225: conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
226:
227: if (conf->filter == NGX_HTTP_IMAGE_OFF) {
228: return ngx_http_next_header_filter(r);
229: }
230:
231: if (r->headers_out.content_type.len
232: >= sizeof("multipart/x-mixed-replace") - 1
233: && ngx_strncasecmp(r->headers_out.content_type.data,
234: (u_char *) "multipart/x-mixed-replace",
235: sizeof("multipart/x-mixed-replace") - 1)
236: == 0)
237: {
238: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
239: "image filter: multipart/x-mixed-replace response");
240:
241: return NGX_ERROR;
242: }
243:
244: ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_image_filter_ctx_t));
245: if (ctx == NULL) {
246: return NGX_ERROR;
247: }
248:
249: ngx_http_set_ctx(r, ctx, ngx_http_image_filter_module);
250:
251: len = r->headers_out.content_length_n;
252:
253: if (len != -1 && len > (off_t) conf->buffer_size) {
254: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
255: "image filter: too big response: %O", len);
256:
257: return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
258: }
259:
260: if (len == -1) {
261: ctx->length = conf->buffer_size;
262:
263: } else {
264: ctx->length = (size_t) len;
265: }
266:
267: if (r->headers_out.refresh) {
268: r->headers_out.refresh->hash = 0;
269: }
270:
271: r->main_filter_need_in_memory = 1;
272: r->allow_ranges = 0;
273:
274: return NGX_OK;
275: }
276:
277:
278: static ngx_int_t
279: ngx_http_image_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
280: {
281: ngx_int_t rc;
282: ngx_str_t *ct;
283: ngx_chain_t out;
284: ngx_http_image_filter_ctx_t *ctx;
285: ngx_http_image_filter_conf_t *conf;
286:
287: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "image filter");
288:
289: if (in == NULL) {
290: return ngx_http_next_body_filter(r, in);
291: }
292:
293: ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
294:
295: if (ctx == NULL) {
296: return ngx_http_next_body_filter(r, in);
297: }
298:
299: switch (ctx->phase) {
300:
301: case NGX_HTTP_IMAGE_START:
302:
303: ctx->type = ngx_http_image_test(r, in);
304:
305: conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
306:
307: if (ctx->type == NGX_HTTP_IMAGE_NONE) {
308:
309: if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
310: out.buf = ngx_http_image_json(r, NULL);
311:
312: if (out.buf) {
313: out.next = NULL;
314: ctx->phase = NGX_HTTP_IMAGE_DONE;
315:
316: return ngx_http_image_send(r, ctx, &out);
317: }
318: }
319:
320: return ngx_http_filter_finalize_request(r,
321: &ngx_http_image_filter_module,
322: NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
323: }
324:
325: /* override content type */
326:
327: ct = &ngx_http_image_types[ctx->type - 1];
328: r->headers_out.content_type_len = ct->len;
329: r->headers_out.content_type = *ct;
330: r->headers_out.content_type_lowcase = NULL;
331:
332: if (conf->filter == NGX_HTTP_IMAGE_TEST) {
333: ctx->phase = NGX_HTTP_IMAGE_PASS;
334:
335: return ngx_http_image_send(r, ctx, in);
336: }
337:
338: ctx->phase = NGX_HTTP_IMAGE_READ;
339:
340: /* fall through */
341:
342: case NGX_HTTP_IMAGE_READ:
343:
344: rc = ngx_http_image_read(r, in);
345:
346: if (rc == NGX_AGAIN) {
347: return NGX_OK;
348: }
349:
350: if (rc == NGX_ERROR) {
351: return ngx_http_filter_finalize_request(r,
352: &ngx_http_image_filter_module,
353: NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
354: }
355:
356: /* fall through */
357:
358: case NGX_HTTP_IMAGE_PROCESS:
359:
360: out.buf = ngx_http_image_process(r);
361:
362: if (out.buf == NULL) {
363: return ngx_http_filter_finalize_request(r,
364: &ngx_http_image_filter_module,
365: NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
366: }
367:
368: out.next = NULL;
369: ctx->phase = NGX_HTTP_IMAGE_PASS;
370:
371: return ngx_http_image_send(r, ctx, &out);
372:
373: case NGX_HTTP_IMAGE_PASS:
374:
375: return ngx_http_next_body_filter(r, in);
376:
377: default: /* NGX_HTTP_IMAGE_DONE */
378:
379: rc = ngx_http_next_body_filter(r, NULL);
380:
381: /* NGX_ERROR resets any pending data */
382: return (rc == NGX_OK) ? NGX_ERROR : rc;
383: }
384: }
385:
386:
387: static ngx_int_t
388: ngx_http_image_send(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx,
389: ngx_chain_t *in)
390: {
391: ngx_int_t rc;
392:
393: rc = ngx_http_next_header_filter(r);
394:
395: if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
396: return NGX_ERROR;
397: }
398:
399: rc = ngx_http_next_body_filter(r, in);
400:
401: if (ctx->phase == NGX_HTTP_IMAGE_DONE) {
402: /* NGX_ERROR resets any pending data */
403: return (rc == NGX_OK) ? NGX_ERROR : rc;
404: }
405:
406: return rc;
407: }
408:
409:
410: static ngx_uint_t
411: ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in)
412: {
413: u_char *p;
414:
415: p = in->buf->pos;
416:
417: if (in->buf->last - p < 16) {
418: return NGX_HTTP_IMAGE_NONE;
419: }
420:
421: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
422: "image filter: \"%c%c\"", p[0], p[1]);
423:
424: if (p[0] == 0xff && p[1] == 0xd8) {
425:
426: /* JPEG */
427:
428: return NGX_HTTP_IMAGE_JPEG;
429:
430: } else if (p[0] == 'G' && p[1] == 'I' && p[2] == 'F' && p[3] == '8'
431: && p[5] == 'a')
432: {
433: if (p[4] == '9' || p[4] == '7') {
434: /* GIF */
435: return NGX_HTTP_IMAGE_GIF;
436: }
437:
438: } else if (p[0] == 0x89 && p[1] == 'P' && p[2] == 'N' && p[3] == 'G'
439: && p[4] == 0x0d && p[5] == 0x0a && p[6] == 0x1a && p[7] == 0x0a)
440: {
441: /* PNG */
442:
443: return NGX_HTTP_IMAGE_PNG;
444: }
445:
446: return NGX_HTTP_IMAGE_NONE;
447: }
448:
449:
450: static ngx_int_t
451: ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in)
452: {
453: u_char *p;
454: size_t size, rest;
455: ngx_buf_t *b;
456: ngx_chain_t *cl;
457: ngx_http_image_filter_ctx_t *ctx;
458:
459: ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
460:
461: if (ctx->image == NULL) {
462: ctx->image = ngx_palloc(r->pool, ctx->length);
463: if (ctx->image == NULL) {
464: return NGX_ERROR;
465: }
466:
467: ctx->last = ctx->image;
468: }
469:
470: p = ctx->last;
471:
472: for (cl = in; cl; cl = cl->next) {
473:
474: b = cl->buf;
475: size = b->last - b->pos;
476:
477: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
478: "image buf: %uz", size);
479:
480: rest = ctx->image + ctx->length - p;
481: size = (rest < size) ? rest : size;
482:
483: p = ngx_cpymem(p, b->pos, size);
484: b->pos += size;
485:
486: if (b->last_buf) {
487: ctx->last = p;
488: return NGX_OK;
489: }
490: }
491:
492: ctx->last = p;
493: r->connection->buffered |= NGX_HTTP_IMAGE_BUFFERED;
494:
495: return NGX_AGAIN;
496: }
497:
498:
499: static ngx_buf_t *
500: ngx_http_image_process(ngx_http_request_t *r)
501: {
502: ngx_int_t rc;
503: ngx_http_image_filter_ctx_t *ctx;
504: ngx_http_image_filter_conf_t *conf;
505:
506: r->connection->buffered &= ~NGX_HTTP_IMAGE_BUFFERED;
507:
508: ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
509:
510: rc = ngx_http_image_size(r, ctx);
511:
512: conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
513:
514: if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
515: return ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL);
516: }
517:
518: ctx->angle = ngx_http_image_filter_get_value(r, conf->acv, conf->angle);
519:
520: if (conf->filter == NGX_HTTP_IMAGE_ROTATE) {
521:
522: if (ctx->angle != 90 && ctx->angle != 180 && ctx->angle != 270) {
523: return NULL;
524: }
525:
526: return ngx_http_image_resize(r, ctx);
527: }
528:
529: ctx->max_width = ngx_http_image_filter_get_value(r, conf->wcv, conf->width);
530: if (ctx->max_width == 0) {
531: return NULL;
532: }
533:
534: ctx->max_height = ngx_http_image_filter_get_value(r, conf->hcv,
535: conf->height);
536: if (ctx->max_height == 0) {
537: return NULL;
538: }
539:
540: if (rc == NGX_OK
541: && ctx->width <= ctx->max_width
542: && ctx->height <= ctx->max_height
543: && ctx->angle == 0
544: && !ctx->force)
545: {
546: return ngx_http_image_asis(r, ctx);
547: }
548:
549: return ngx_http_image_resize(r, ctx);
550: }
551:
552:
553: static ngx_buf_t *
554: ngx_http_image_json(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
555: {
556: size_t len;
557: ngx_buf_t *b;
558:
559: b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
560: if (b == NULL) {
561: return NULL;
562: }
563:
564: b->memory = 1;
565: b->last_buf = 1;
566:
567: ngx_http_clean_header(r);
568:
569: r->headers_out.status = NGX_HTTP_OK;
570: ngx_str_set(&r->headers_out.content_type, "text/plain");
571: r->headers_out.content_type_lowcase = NULL;
572:
573: if (ctx == NULL) {
574: b->pos = (u_char *) "{}" CRLF;
575: b->last = b->pos + sizeof("{}" CRLF) - 1;
576:
577: ngx_http_image_length(r, b);
578:
579: return b;
580: }
581:
582: len = sizeof("{ \"img\" : "
583: "{ \"width\": , \"height\": , \"type\": \"jpeg\" } }" CRLF) - 1
584: + 2 * NGX_SIZE_T_LEN;
585:
586: b->pos = ngx_pnalloc(r->pool, len);
587: if (b->pos == NULL) {
588: return NULL;
589: }
590:
591: b->last = ngx_sprintf(b->pos,
592: "{ \"img\" : "
593: "{ \"width\": %uz,"
594: " \"height\": %uz,"
595: " \"type\": \"%s\" } }" CRLF,
596: ctx->width, ctx->height,
597: ngx_http_image_types[ctx->type - 1].data + 6);
598:
599: ngx_http_image_length(r, b);
600:
601: return b;
602: }
603:
604:
605: static ngx_buf_t *
606: ngx_http_image_asis(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
607: {
608: ngx_buf_t *b;
609:
610: b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
611: if (b == NULL) {
612: return NULL;
613: }
614:
615: b->pos = ctx->image;
616: b->last = ctx->last;
617: b->memory = 1;
618: b->last_buf = 1;
619:
620: ngx_http_image_length(r, b);
621:
622: return b;
623: }
624:
625:
626: static void
627: ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b)
628: {
629: r->headers_out.content_length_n = b->last - b->pos;
630:
631: if (r->headers_out.content_length) {
632: r->headers_out.content_length->hash = 0;
633: }
634:
635: r->headers_out.content_length = NULL;
636: }
637:
638:
639: static ngx_int_t
640: ngx_http_image_size(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
641: {
642: u_char *p, *last;
643: size_t len, app;
644: ngx_uint_t width, height;
645:
646: p = ctx->image;
647:
648: switch (ctx->type) {
649:
650: case NGX_HTTP_IMAGE_JPEG:
651:
652: p += 2;
653: last = ctx->image + ctx->length - 10;
654: width = 0;
655: height = 0;
656: app = 0;
657:
658: while (p < last) {
659:
660: if (p[0] == 0xff && p[1] != 0xff) {
661:
662: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
663: "JPEG: %02xd %02xd", p[0], p[1]);
664:
665: p++;
666:
667: if ((*p == 0xc0 || *p == 0xc1 || *p == 0xc2 || *p == 0xc3
668: || *p == 0xc9 || *p == 0xca || *p == 0xcb)
669: && (width == 0 || height == 0))
670: {
671: width = p[6] * 256 + p[7];
672: height = p[4] * 256 + p[5];
673: }
674:
675: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
676: "JPEG: %02xd %02xd", p[1], p[2]);
677:
678: len = p[1] * 256 + p[2];
679:
680: if (*p >= 0xe1 && *p <= 0xef) {
681: /* application data, e.g., EXIF, Adobe XMP, etc. */
682: app += len;
683: }
684:
685: p += len;
686:
687: continue;
688: }
689:
690: p++;
691: }
692:
693: if (width == 0 || height == 0) {
694: return NGX_DECLINED;
695: }
696:
697: if (ctx->length / 20 < app) {
698: /* force conversion if application data consume more than 5% */
699: ctx->force = 1;
700: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
701: "app data size: %uz", app);
702: }
703:
704: break;
705:
706: case NGX_HTTP_IMAGE_GIF:
707:
708: if (ctx->length < 10) {
709: return NGX_DECLINED;
710: }
711:
712: width = p[7] * 256 + p[6];
713: height = p[9] * 256 + p[8];
714:
715: break;
716:
717: case NGX_HTTP_IMAGE_PNG:
718:
719: if (ctx->length < 24) {
720: return NGX_DECLINED;
721: }
722:
723: width = p[18] * 256 + p[19];
724: height = p[22] * 256 + p[23];
725:
726: break;
727:
728: default:
729:
730: return NGX_DECLINED;
731: }
732:
733: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
734: "image size: %d x %d", width, height);
735:
736: ctx->width = width;
737: ctx->height = height;
738:
739: return NGX_OK;
740: }
741:
742:
743: static ngx_buf_t *
744: ngx_http_image_resize(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
745: {
746: int sx, sy, dx, dy, ox, oy, ax, ay, size,
747: colors, palette, transparent, sharpen,
748: red, green, blue, t;
749: u_char *out;
750: ngx_buf_t *b;
751: ngx_uint_t resize;
752: gdImagePtr src, dst;
753: ngx_pool_cleanup_t *cln;
754: ngx_http_image_filter_conf_t *conf;
755:
756: src = ngx_http_image_source(r, ctx);
757:
758: if (src == NULL) {
759: return NULL;
760: }
761:
762: sx = gdImageSX(src);
763: sy = gdImageSY(src);
764:
765: conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
766:
767: if (!ctx->force
768: && ctx->angle == 0
769: && (ngx_uint_t) sx <= ctx->max_width
770: && (ngx_uint_t) sy <= ctx->max_height)
771: {
772: gdImageDestroy(src);
773: return ngx_http_image_asis(r, ctx);
774: }
775:
776: colors = gdImageColorsTotal(src);
777:
778: if (colors && conf->transparency) {
779: transparent = gdImageGetTransparent(src);
780:
781: if (transparent != -1) {
782: palette = colors;
783: red = gdImageRed(src, transparent);
784: green = gdImageGreen(src, transparent);
785: blue = gdImageBlue(src, transparent);
786:
787: goto transparent;
788: }
789: }
790:
791: palette = 0;
792: transparent = -1;
793: red = 0;
794: green = 0;
795: blue = 0;
796:
797: transparent:
798:
799: gdImageColorTransparent(src, -1);
800:
801: dx = sx;
802: dy = sy;
803:
804: if (conf->filter == NGX_HTTP_IMAGE_RESIZE) {
805:
806: if ((ngx_uint_t) dx > ctx->max_width) {
807: dy = dy * ctx->max_width / dx;
808: dy = dy ? dy : 1;
809: dx = ctx->max_width;
810: }
811:
812: if ((ngx_uint_t) dy > ctx->max_height) {
813: dx = dx * ctx->max_height / dy;
814: dx = dx ? dx : 1;
815: dy = ctx->max_height;
816: }
817:
818: resize = 1;
819:
820: } else if (conf->filter == NGX_HTTP_IMAGE_ROTATE) {
821:
822: resize = 0;
823:
824: } else { /* NGX_HTTP_IMAGE_CROP */
825:
826: resize = 0;
827:
828: if ((double) dx / dy < (double) ctx->max_width / ctx->max_height) {
829: if ((ngx_uint_t) dx > ctx->max_width) {
830: dy = dy * ctx->max_width / dx;
831: dy = dy ? dy : 1;
832: dx = ctx->max_width;
833: resize = 1;
834: }
835:
836: } else {
837: if ((ngx_uint_t) dy > ctx->max_height) {
838: dx = dx * ctx->max_height / dy;
839: dx = dx ? dx : 1;
840: dy = ctx->max_height;
841: resize = 1;
842: }
843: }
844: }
845:
846: if (resize) {
847: dst = ngx_http_image_new(r, dx, dy, palette);
848: if (dst == NULL) {
849: gdImageDestroy(src);
850: return NULL;
851: }
852:
853: if (colors == 0) {
854: gdImageSaveAlpha(dst, 1);
855: gdImageAlphaBlending(dst, 0);
856: }
857:
858: gdImageCopyResampled(dst, src, 0, 0, 0, 0, dx, dy, sx, sy);
859:
860: if (colors) {
861: gdImageTrueColorToPalette(dst, 1, 256);
862: }
863:
864: gdImageDestroy(src);
865:
866: } else {
867: dst = src;
868: }
869:
870: if (ctx->angle) {
871: src = dst;
872:
873: ax = (dx % 2 == 0) ? 1 : 0;
874: ay = (dy % 2 == 0) ? 1 : 0;
875:
876: switch (ctx->angle) {
877:
878: case 90:
879: case 270:
880: dst = ngx_http_image_new(r, dy, dx, palette);
881: if (dst == NULL) {
882: gdImageDestroy(src);
883: return NULL;
884: }
885: if (ctx->angle == 90) {
886: ox = dy / 2 + ay;
887: oy = dx / 2 - ax;
888:
889: } else {
890: ox = dy / 2 - ay;
891: oy = dx / 2 + ax;
892: }
893:
894: gdImageCopyRotated(dst, src, ox, oy, 0, 0,
895: dx + ax, dy + ay, ctx->angle);
896: gdImageDestroy(src);
897:
898: t = dx;
899: dx = dy;
900: dy = t;
901: break;
902:
903: case 180:
904: dst = ngx_http_image_new(r, dx, dy, palette);
905: if (dst == NULL) {
906: gdImageDestroy(src);
907: return NULL;
908: }
909: gdImageCopyRotated(dst, src, dx / 2 - ax, dy / 2 - ay, 0, 0,
910: dx + ax, dy + ay, ctx->angle);
911: gdImageDestroy(src);
912: break;
913: }
914: }
915:
916: if (conf->filter == NGX_HTTP_IMAGE_CROP) {
917:
918: src = dst;
919:
920: if ((ngx_uint_t) dx > ctx->max_width) {
921: ox = dx - ctx->max_width;
922:
923: } else {
924: ox = 0;
925: }
926:
927: if ((ngx_uint_t) dy > ctx->max_height) {
928: oy = dy - ctx->max_height;
929:
930: } else {
931: oy = 0;
932: }
933:
934: if (ox || oy) {
935:
936: dst = ngx_http_image_new(r, dx - ox, dy - oy, colors);
937:
938: if (dst == NULL) {
939: gdImageDestroy(src);
940: return NULL;
941: }
942:
943: ox /= 2;
944: oy /= 2;
945:
946: ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
947: "image crop: %d x %d @ %d x %d",
948: dx, dy, ox, oy);
949:
950: if (colors == 0) {
951: gdImageSaveAlpha(dst, 1);
952: gdImageAlphaBlending(dst, 0);
953: }
954:
955: gdImageCopy(dst, src, 0, 0, ox, oy, dx - ox, dy - oy);
956:
957: if (colors) {
958: gdImageTrueColorToPalette(dst, 1, 256);
959: }
960:
961: gdImageDestroy(src);
962: }
963: }
964:
965: if (transparent != -1 && colors) {
966: gdImageColorTransparent(dst, gdImageColorExact(dst, red, green, blue));
967: }
968:
969: sharpen = ngx_http_image_filter_get_value(r, conf->shcv, conf->sharpen);
970: if (sharpen > 0) {
971: gdImageSharpen(dst, sharpen);
972: }
973:
974: gdImageInterlace(dst, (int) conf->interlace);
975:
976: out = ngx_http_image_out(r, ctx->type, dst, &size);
977:
978: ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
979: "image: %d x %d %d", sx, sy, colors);
980:
981: gdImageDestroy(dst);
982: ngx_pfree(r->pool, ctx->image);
983:
984: if (out == NULL) {
985: return NULL;
986: }
987:
988: cln = ngx_pool_cleanup_add(r->pool, 0);
989: if (cln == NULL) {
990: gdFree(out);
991: return NULL;
992: }
993:
994: b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
995: if (b == NULL) {
996: gdFree(out);
997: return NULL;
998: }
999:
1000: cln->handler = ngx_http_image_cleanup;
1001: cln->data = out;
1002:
1003: b->pos = out;
1004: b->last = out + size;
1005: b->memory = 1;
1006: b->last_buf = 1;
1007:
1008: ngx_http_image_length(r, b);
1009:
1010: return b;
1011: }
1012:
1013:
1014: static gdImagePtr
1015: ngx_http_image_source(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
1016: {
1017: char *failed;
1018: gdImagePtr img;
1019:
1020: img = NULL;
1021:
1022: switch (ctx->type) {
1023:
1024: case NGX_HTTP_IMAGE_JPEG:
1025: img = gdImageCreateFromJpegPtr(ctx->length, ctx->image);
1026: failed = "gdImageCreateFromJpegPtr() failed";
1027: break;
1028:
1029: case NGX_HTTP_IMAGE_GIF:
1030: img = gdImageCreateFromGifPtr(ctx->length, ctx->image);
1031: failed = "gdImageCreateFromGifPtr() failed";
1032: break;
1033:
1034: case NGX_HTTP_IMAGE_PNG:
1035: img = gdImageCreateFromPngPtr(ctx->length, ctx->image);
1036: failed = "gdImageCreateFromPngPtr() failed";
1037: break;
1038:
1039: default:
1040: failed = "unknown image type";
1041: break;
1042: }
1043:
1044: if (img == NULL) {
1045: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);
1046: }
1047:
1048: return img;
1049: }
1050:
1051:
1052: static gdImagePtr
1053: ngx_http_image_new(ngx_http_request_t *r, int w, int h, int colors)
1054: {
1055: gdImagePtr img;
1056:
1057: if (colors == 0) {
1058: img = gdImageCreateTrueColor(w, h);
1059:
1060: if (img == NULL) {
1061: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1062: "gdImageCreateTrueColor() failed");
1063: return NULL;
1064: }
1065:
1066: } else {
1067: img = gdImageCreate(w, h);
1068:
1069: if (img == NULL) {
1070: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1071: "gdImageCreate() failed");
1072: return NULL;
1073: }
1074: }
1075:
1076: return img;
1077: }
1078:
1079:
1080: static u_char *
1081: ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img,
1082: int *size)
1083: {
1084: char *failed;
1085: u_char *out;
1086: ngx_int_t jq;
1087: ngx_http_image_filter_conf_t *conf;
1088:
1089: out = NULL;
1090:
1091: switch (type) {
1092:
1093: case NGX_HTTP_IMAGE_JPEG:
1094: conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
1095:
1096: jq = ngx_http_image_filter_get_value(r, conf->jqcv, conf->jpeg_quality);
1097: if (jq <= 0) {
1098: return NULL;
1099: }
1100:
1101: out = gdImageJpegPtr(img, size, jq);
1102: failed = "gdImageJpegPtr() failed";
1103: break;
1104:
1105: case NGX_HTTP_IMAGE_GIF:
1106: out = gdImageGifPtr(img, size);
1107: failed = "gdImageGifPtr() failed";
1108: break;
1109:
1110: case NGX_HTTP_IMAGE_PNG:
1111: out = gdImagePngPtr(img, size);
1112: failed = "gdImagePngPtr() failed";
1113: break;
1114:
1115: default:
1116: failed = "unknown image type";
1117: break;
1118: }
1119:
1120: if (out == NULL) {
1121: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);
1122: }
1123:
1124: return out;
1125: }
1126:
1127:
1128: static void
1129: ngx_http_image_cleanup(void *data)
1130: {
1131: gdFree(data);
1132: }
1133:
1134:
1135: static ngx_uint_t
1136: ngx_http_image_filter_get_value(ngx_http_request_t *r,
1137: ngx_http_complex_value_t *cv, ngx_uint_t v)
1138: {
1139: ngx_str_t val;
1140:
1141: if (cv == NULL) {
1142: return v;
1143: }
1144:
1145: if (ngx_http_complex_value(r, cv, &val) != NGX_OK) {
1146: return 0;
1147: }
1148:
1149: return ngx_http_image_filter_value(&val);
1150: }
1151:
1152:
1153: static ngx_uint_t
1154: ngx_http_image_filter_value(ngx_str_t *value)
1155: {
1156: ngx_int_t n;
1157:
1158: if (value->len == 1 && value->data[0] == '-') {
1159: return (ngx_uint_t) -1;
1160: }
1161:
1162: n = ngx_atoi(value->data, value->len);
1163:
1164: if (n > 0) {
1165: return (ngx_uint_t) n;
1166: }
1167:
1168: return 0;
1169: }
1170:
1171:
1172: static void *
1173: ngx_http_image_filter_create_conf(ngx_conf_t *cf)
1174: {
1175: ngx_http_image_filter_conf_t *conf;
1176:
1177: conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_image_filter_conf_t));
1178: if (conf == NULL) {
1179: return NULL;
1180: }
1181:
1182: /*
1183: * set by ngx_pcalloc():
1184: *
1185: * conf->width = 0;
1186: * conf->height = 0;
1187: * conf->angle = 0;
1188: * conf->wcv = NULL;
1189: * conf->hcv = NULL;
1190: * conf->acv = NULL;
1191: * conf->jqcv = NULL;
1192: * conf->shcv = NULL;
1193: */
1194:
1195: conf->filter = NGX_CONF_UNSET_UINT;
1196: conf->jpeg_quality = NGX_CONF_UNSET_UINT;
1197: conf->sharpen = NGX_CONF_UNSET_UINT;
1198: conf->transparency = NGX_CONF_UNSET;
1199: conf->interlace = NGX_CONF_UNSET;
1200: conf->buffer_size = NGX_CONF_UNSET_SIZE;
1201:
1202: return conf;
1203: }
1204:
1205:
1206: static char *
1207: ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
1208: {
1209: ngx_http_image_filter_conf_t *prev = parent;
1210: ngx_http_image_filter_conf_t *conf = child;
1211:
1212: if (conf->filter == NGX_CONF_UNSET_UINT) {
1213:
1214: if (prev->filter == NGX_CONF_UNSET_UINT) {
1215: conf->filter = NGX_HTTP_IMAGE_OFF;
1216:
1217: } else {
1218: conf->filter = prev->filter;
1219: conf->width = prev->width;
1220: conf->height = prev->height;
1221: conf->angle = prev->angle;
1222: conf->wcv = prev->wcv;
1223: conf->hcv = prev->hcv;
1224: conf->acv = prev->acv;
1225: }
1226: }
1227:
1228: if (conf->jpeg_quality == NGX_CONF_UNSET_UINT) {
1229:
1230: /* 75 is libjpeg default quality */
1231: ngx_conf_merge_uint_value(conf->jpeg_quality, prev->jpeg_quality, 75);
1232:
1233: if (conf->jqcv == NULL) {
1234: conf->jqcv = prev->jqcv;
1235: }
1236: }
1237:
1238: if (conf->sharpen == NGX_CONF_UNSET_UINT) {
1239: ngx_conf_merge_uint_value(conf->sharpen, prev->sharpen, 0);
1240:
1241: if (conf->shcv == NULL) {
1242: conf->shcv = prev->shcv;
1243: }
1244: }
1245:
1246: ngx_conf_merge_value(conf->transparency, prev->transparency, 1);
1247:
1248: ngx_conf_merge_value(conf->interlace, prev->interlace, 0);
1249:
1250: ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
1251: 1 * 1024 * 1024);
1252:
1253: return NGX_CONF_OK;
1254: }
1255:
1256:
1257: static char *
1258: ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1259: {
1260: ngx_http_image_filter_conf_t *imcf = conf;
1261:
1262: ngx_str_t *value;
1263: ngx_int_t n;
1264: ngx_uint_t i;
1265: ngx_http_complex_value_t cv;
1266: ngx_http_compile_complex_value_t ccv;
1267:
1268: value = cf->args->elts;
1269:
1270: i = 1;
1271:
1272: if (cf->args->nelts == 2) {
1273: if (ngx_strcmp(value[i].data, "off") == 0) {
1274: imcf->filter = NGX_HTTP_IMAGE_OFF;
1275:
1276: } else if (ngx_strcmp(value[i].data, "test") == 0) {
1277: imcf->filter = NGX_HTTP_IMAGE_TEST;
1278:
1279: } else if (ngx_strcmp(value[i].data, "size") == 0) {
1280: imcf->filter = NGX_HTTP_IMAGE_SIZE;
1281:
1282: } else {
1283: goto failed;
1284: }
1285:
1286: return NGX_CONF_OK;
1287:
1288: } else if (cf->args->nelts == 3) {
1289:
1290: if (ngx_strcmp(value[i].data, "rotate") == 0) {
1291: if (imcf->filter != NGX_HTTP_IMAGE_RESIZE
1292: && imcf->filter != NGX_HTTP_IMAGE_CROP)
1293: {
1294: imcf->filter = NGX_HTTP_IMAGE_ROTATE;
1295: }
1296:
1297: ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
1298:
1299: ccv.cf = cf;
1300: ccv.value = &value[++i];
1301: ccv.complex_value = &cv;
1302:
1303: if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
1304: return NGX_CONF_ERROR;
1305: }
1306:
1307: if (cv.lengths == NULL) {
1308: n = ngx_http_image_filter_value(&value[i]);
1309:
1310: if (n != 90 && n != 180 && n != 270) {
1311: goto failed;
1312: }
1313:
1314: imcf->angle = (ngx_uint_t) n;
1315:
1316: } else {
1317: imcf->acv = ngx_palloc(cf->pool,
1318: sizeof(ngx_http_complex_value_t));
1319: if (imcf->acv == NULL) {
1320: return NGX_CONF_ERROR;
1321: }
1322:
1323: *imcf->acv = cv;
1324: }
1325:
1326: return NGX_CONF_OK;
1327:
1328: } else {
1329: goto failed;
1330: }
1331: }
1332:
1333: if (ngx_strcmp(value[i].data, "resize") == 0) {
1334: imcf->filter = NGX_HTTP_IMAGE_RESIZE;
1335:
1336: } else if (ngx_strcmp(value[i].data, "crop") == 0) {
1337: imcf->filter = NGX_HTTP_IMAGE_CROP;
1338:
1339: } else {
1340: goto failed;
1341: }
1342:
1343: ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
1344:
1345: ccv.cf = cf;
1346: ccv.value = &value[++i];
1347: ccv.complex_value = &cv;
1348:
1349: if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
1350: return NGX_CONF_ERROR;
1351: }
1352:
1353: if (cv.lengths == NULL) {
1354: n = ngx_http_image_filter_value(&value[i]);
1355:
1356: if (n == 0) {
1357: goto failed;
1358: }
1359:
1360: imcf->width = (ngx_uint_t) n;
1361:
1362: } else {
1363: imcf->wcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
1364: if (imcf->wcv == NULL) {
1365: return NGX_CONF_ERROR;
1366: }
1367:
1368: *imcf->wcv = cv;
1369: }
1370:
1371: ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
1372:
1373: ccv.cf = cf;
1374: ccv.value = &value[++i];
1375: ccv.complex_value = &cv;
1376:
1377: if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
1378: return NGX_CONF_ERROR;
1379: }
1380:
1381: if (cv.lengths == NULL) {
1382: n = ngx_http_image_filter_value(&value[i]);
1383:
1384: if (n == 0) {
1385: goto failed;
1386: }
1387:
1388: imcf->height = (ngx_uint_t) n;
1389:
1390: } else {
1391: imcf->hcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
1392: if (imcf->hcv == NULL) {
1393: return NGX_CONF_ERROR;
1394: }
1395:
1396: *imcf->hcv = cv;
1397: }
1398:
1399: return NGX_CONF_OK;
1400:
1401: failed:
1402:
1403: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
1404: &value[i]);
1405:
1406: return NGX_CONF_ERROR;
1407: }
1408:
1409:
1410: static char *
1411: ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf, ngx_command_t *cmd,
1412: void *conf)
1413: {
1414: ngx_http_image_filter_conf_t *imcf = conf;
1415:
1416: ngx_str_t *value;
1417: ngx_int_t n;
1418: ngx_http_complex_value_t cv;
1419: ngx_http_compile_complex_value_t ccv;
1420:
1421: value = cf->args->elts;
1422:
1423: ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
1424:
1425: ccv.cf = cf;
1426: ccv.value = &value[1];
1427: ccv.complex_value = &cv;
1428:
1429: if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
1430: return NGX_CONF_ERROR;
1431: }
1432:
1433: if (cv.lengths == NULL) {
1434: n = ngx_http_image_filter_value(&value[1]);
1435:
1436: if (n <= 0) {
1437: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1438: "invalid value \"%V\"", &value[1]);
1439: return NGX_CONF_ERROR;
1440: }
1441:
1442: imcf->jpeg_quality = (ngx_uint_t) n;
1443:
1444: } else {
1445: imcf->jqcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
1446: if (imcf->jqcv == NULL) {
1447: return NGX_CONF_ERROR;
1448: }
1449:
1450: *imcf->jqcv = cv;
1451: }
1452:
1453: return NGX_CONF_OK;
1454: }
1455:
1456:
1457: static char *
1458: ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,
1459: void *conf)
1460: {
1461: ngx_http_image_filter_conf_t *imcf = conf;
1462:
1463: ngx_str_t *value;
1464: ngx_int_t n;
1465: ngx_http_complex_value_t cv;
1466: ngx_http_compile_complex_value_t ccv;
1467:
1468: value = cf->args->elts;
1469:
1470: ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
1471:
1472: ccv.cf = cf;
1473: ccv.value = &value[1];
1474: ccv.complex_value = &cv;
1475:
1476: if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
1477: return NGX_CONF_ERROR;
1478: }
1479:
1480: if (cv.lengths == NULL) {
1481: n = ngx_http_image_filter_value(&value[1]);
1482:
1483: if (n < 0) {
1484: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1485: "invalid value \"%V\"", &value[1]);
1486: return NGX_CONF_ERROR;
1487: }
1488:
1489: imcf->sharpen = (ngx_uint_t) n;
1490:
1491: } else {
1492: imcf->shcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
1493: if (imcf->shcv == NULL) {
1494: return NGX_CONF_ERROR;
1495: }
1496:
1497: *imcf->shcv = cv;
1498: }
1499:
1500: return NGX_CONF_OK;
1501: }
1502:
1503:
1504: static ngx_int_t
1505: ngx_http_image_filter_init(ngx_conf_t *cf)
1506: {
1507: ngx_http_next_header_filter = ngx_http_top_header_filter;
1508: ngx_http_top_header_filter = ngx_http_image_header_filter;
1509:
1510: ngx_http_next_body_filter = ngx_http_top_body_filter;
1511: ngx_http_top_body_filter = ngx_http_image_body_filter;
1512:
1513: return NGX_OK;
1514: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>