upstream nginx-0.7.31
[nginx.git] / nginx / src / http / modules / ngx_http_chunked_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_chunked_filter_init(ngx_conf_t *cf);
13
14
15 static ngx_http_module_t  ngx_http_chunked_filter_module_ctx = {
16     NULL,                                  /* preconfiguration */
17     ngx_http_chunked_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_chunked_filter_module = {
31     NGX_MODULE_V1,
32     &ngx_http_chunked_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 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
47 static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
48
49
50 static ngx_int_t
51 ngx_http_chunked_header_filter(ngx_http_request_t *r)
52 {
53     if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED
54         || r->headers_out.status == NGX_HTTP_NO_CONTENT
55         || r->headers_out.status == NGX_HTTP_CREATED
56         || r != r->main
57         || (r->method & NGX_HTTP_HEAD))
58     {
59         return ngx_http_next_header_filter(r);
60     }
61
62     if (r->headers_out.content_length_n == -1) {
63         if (r->http_version < NGX_HTTP_VERSION_11) {
64             r->keepalive = 0;
65
66         } else {
67             r->chunked = 1;
68         }
69     }
70
71     return ngx_http_next_header_filter(r);
72 }
73
74
75 static ngx_int_t
76 ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
77 {
78     u_char       *chunk;
79     off_t         size;
80     ngx_buf_t    *b;
81     ngx_chain_t   out, tail, *cl, *tl, **ll;
82
83     if (in == NULL || !r->chunked || r->header_only) {
84         return ngx_http_next_body_filter(r, in);
85     }
86
87     out.buf = NULL;
88     ll = &out.next;
89
90     size = 0;
91     cl = in;
92
93     for ( ;; ) {
94         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
95                        "http chunk: %d", ngx_buf_size(cl->buf));
96
97         size += ngx_buf_size(cl->buf);
98
99         if (cl->buf->flush
100             || cl->buf->sync
101             || ngx_buf_in_memory(cl->buf)
102             || cl->buf->in_file)
103         {
104             tl = ngx_alloc_chain_link(r->pool);
105             if (tl == NULL) {
106                 return NGX_ERROR;
107             }
108
109             tl->buf = cl->buf;
110             *ll = tl;
111             ll = &tl->next;
112         }
113
114         if (cl->next == NULL) {
115             break;
116         }
117
118         cl = cl->next;
119     }
120
121     if (size) {
122         b = ngx_calloc_buf(r->pool);
123         if (b == NULL) {
124             return NGX_ERROR;
125         }
126
127         /* the "0000000000000000" is 64-bit hexadimal string */
128
129         chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1);
130         if (chunk == NULL) {
131             return NGX_ERROR;
132         }
133
134         b->temporary = 1;
135         b->pos = chunk;
136         b->last = ngx_sprintf(chunk, "%xO" CRLF, size);
137
138         out.buf = b;
139     }
140
141     if (cl->buf->last_buf) {
142         b = ngx_calloc_buf(r->pool);
143         if (b == NULL) {
144             return NGX_ERROR;
145         }
146
147         b->memory = 1;
148         b->last_buf = 1;
149         b->pos = (u_char *) CRLF "0" CRLF CRLF;
150         b->last = b->pos + 7;
151
152         cl->buf->last_buf = 0;
153
154         if (size == 0) {
155             b->pos += 2;
156             out.buf = b;
157             out.next = NULL;
158
159             return ngx_http_next_body_filter(r, &out);
160         }
161
162     } else {
163         if (size == 0) {
164             *ll = NULL;
165             return ngx_http_next_body_filter(r, out.next);
166         }
167
168         b = ngx_calloc_buf(r->pool);
169         if (b == NULL) {
170             return NGX_ERROR;
171         }
172
173         b->memory = 1;
174         b->pos = (u_char *) CRLF;
175         b->last = b->pos + 2;
176     }
177
178     tail.buf = b;
179     tail.next = NULL;
180     *ll = &tail;
181
182     return ngx_http_next_body_filter(r, &out);
183 }
184
185
186 static ngx_int_t
187 ngx_http_chunked_filter_init(ngx_conf_t *cf)
188 {
189     ngx_http_next_header_filter = ngx_http_top_header_filter;
190     ngx_http_top_header_filter = ngx_http_chunked_header_filter;
191
192     ngx_http_next_body_filter = ngx_http_top_body_filter;
193     ngx_http_top_body_filter = ngx_http_chunked_body_filter;
194
195     return NGX_OK;
196 }