Mercurial > hg > nginx-vendor-current
comparison src/http/ngx_http_request_body.c @ 692:6db6e93f55ee NGINX_1_3_9
nginx 1.3.9
*) Feature: support for chunked transfer encoding while reading client
request body.
*) Feature: the $request_time and $msec variables can now be used not
only in the "log_format" directive.
*) Bugfix: cache manager and cache loader processes might not be able to
start if more than 512 listen sockets were used.
*) Bugfix: in the ngx_http_dav_module.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Tue, 27 Nov 2012 00:00:00 +0400 |
parents | f41d4b305d22 |
children |
comparison
equal
deleted
inserted
replaced
691:acfd484db0ca | 692:6db6e93f55ee |
---|---|
10 #include <ngx_http.h> | 10 #include <ngx_http.h> |
11 | 11 |
12 | 12 |
13 static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r); | 13 static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r); |
14 static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r); | 14 static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r); |
15 static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r, | 15 static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r); |
16 ngx_chain_t *body); | |
17 static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r); | 16 static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r); |
17 static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r, | |
18 ngx_buf_t *b); | |
18 static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r); | 19 static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r); |
19 | 20 |
20 | 21 static ngx_int_t ngx_http_request_body_filter(ngx_http_request_t *r, |
21 /* | 22 ngx_chain_t *in); |
22 * on completion ngx_http_read_client_request_body() adds to | 23 static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r, |
23 * r->request_body->bufs one or two bufs: | 24 ngx_chain_t *in); |
24 * *) one memory buf that was preread in r->header_in; | 25 static ngx_int_t ngx_http_request_body_chunked_filter(ngx_http_request_t *r, |
25 * *) one memory or file buf that contains the rest of the body | 26 ngx_chain_t *in); |
26 */ | 27 static ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r, |
28 ngx_chain_t *in); | |
29 | |
27 | 30 |
28 ngx_int_t | 31 ngx_int_t |
29 ngx_http_read_client_request_body(ngx_http_request_t *r, | 32 ngx_http_read_client_request_body(ngx_http_request_t *r, |
30 ngx_http_client_body_handler_pt post_handler) | 33 ngx_http_client_body_handler_pt post_handler) |
31 { | 34 { |
32 size_t preread; | 35 size_t preread; |
33 ssize_t size; | 36 ssize_t size; |
34 ngx_buf_t *b; | 37 ngx_int_t rc; |
35 ngx_chain_t *cl, **next; | 38 ngx_chain_t out; |
36 ngx_temp_file_t *tf; | |
37 ngx_http_request_body_t *rb; | 39 ngx_http_request_body_t *rb; |
38 ngx_http_core_loc_conf_t *clcf; | 40 ngx_http_core_loc_conf_t *clcf; |
39 | 41 |
40 r->main->count++; | 42 r->main->count++; |
41 | 43 |
43 post_handler(r); | 45 post_handler(r); |
44 return NGX_OK; | 46 return NGX_OK; |
45 } | 47 } |
46 | 48 |
47 if (ngx_http_test_expect(r) != NGX_OK) { | 49 if (ngx_http_test_expect(r) != NGX_OK) { |
48 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 50 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; |
51 goto done; | |
49 } | 52 } |
50 | 53 |
51 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); | 54 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); |
52 if (rb == NULL) { | 55 if (rb == NULL) { |
53 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 56 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; |
54 } | 57 goto done; |
55 | 58 } |
56 r->request_body = rb; | |
57 | |
58 if (r->headers_in.content_length_n < 0) { | |
59 post_handler(r); | |
60 return NGX_OK; | |
61 } | |
62 | |
63 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
64 | |
65 if (r->headers_in.content_length_n == 0) { | |
66 | |
67 if (r->request_body_in_file_only) { | |
68 tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); | |
69 if (tf == NULL) { | |
70 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
71 } | |
72 | |
73 tf->file.fd = NGX_INVALID_FILE; | |
74 tf->file.log = r->connection->log; | |
75 tf->path = clcf->client_body_temp_path; | |
76 tf->pool = r->pool; | |
77 tf->warn = "a client request body is buffered to a temporary file"; | |
78 tf->log_level = r->request_body_file_log_level; | |
79 tf->persistent = r->request_body_in_persistent_file; | |
80 tf->clean = r->request_body_in_clean_file; | |
81 | |
82 if (r->request_body_file_group_access) { | |
83 tf->access = 0660; | |
84 } | |
85 | |
86 rb->temp_file = tf; | |
87 | |
88 if (ngx_create_temp_file(&tf->file, tf->path, tf->pool, | |
89 tf->persistent, tf->clean, tf->access) | |
90 != NGX_OK) | |
91 { | |
92 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
93 } | |
94 } | |
95 | |
96 post_handler(r); | |
97 | |
98 return NGX_OK; | |
99 } | |
100 | |
101 rb->post_handler = post_handler; | |
102 | 59 |
103 /* | 60 /* |
104 * set by ngx_pcalloc(): | 61 * set by ngx_pcalloc(): |
105 * | 62 * |
106 * rb->bufs = NULL; | 63 * rb->bufs = NULL; |
107 * rb->buf = NULL; | 64 * rb->buf = NULL; |
108 * rb->rest = 0; | 65 * rb->free = NULL; |
66 * rb->busy = NULL; | |
67 * rb->chunked = NULL; | |
109 */ | 68 */ |
69 | |
70 rb->rest = -1; | |
71 rb->post_handler = post_handler; | |
72 | |
73 r->request_body = rb; | |
74 | |
75 if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) { | |
76 post_handler(r); | |
77 return NGX_OK; | |
78 } | |
110 | 79 |
111 preread = r->header_in->last - r->header_in->pos; | 80 preread = r->header_in->last - r->header_in->pos; |
112 | 81 |
113 if (preread) { | 82 if (preread) { |
114 | 83 |
115 /* there is the pre-read part of the request body */ | 84 /* there is the pre-read part of the request body */ |
116 | 85 |
117 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 86 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
118 "http client request body preread %uz", preread); | 87 "http client request body preread %uz", preread); |
119 | 88 |
120 b = ngx_calloc_buf(r->pool); | 89 out.buf = r->header_in; |
121 if (b == NULL) { | 90 out.next = NULL; |
122 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 91 |
123 } | 92 rc = ngx_http_request_body_filter(r, &out); |
124 | 93 |
125 b->temporary = 1; | 94 if (rc != NGX_OK) { |
126 b->start = r->header_in->pos; | 95 goto done; |
127 b->pos = r->header_in->pos; | 96 } |
128 b->last = r->header_in->last; | 97 |
129 b->end = r->header_in->end; | 98 r->request_length += preread - (r->header_in->last - r->header_in->pos); |
130 | 99 |
131 rb->bufs = ngx_alloc_chain_link(r->pool); | 100 if (!r->headers_in.chunked |
132 if (rb->bufs == NULL) { | 101 && rb->rest > 0 |
133 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 102 && rb->rest <= (off_t) (r->header_in->end - r->header_in->last)) |
134 } | 103 { |
135 | |
136 rb->bufs->buf = b; | |
137 rb->bufs->next = NULL; | |
138 | |
139 rb->buf = b; | |
140 | |
141 if ((off_t) preread >= r->headers_in.content_length_n) { | |
142 | |
143 /* the whole request body was pre-read */ | |
144 | |
145 r->header_in->pos += (size_t) r->headers_in.content_length_n; | |
146 r->request_length += r->headers_in.content_length_n; | |
147 b->last = r->header_in->pos; | |
148 | |
149 if (r->request_body_in_file_only) { | |
150 if (ngx_http_write_request_body(r, rb->bufs) != NGX_OK) { | |
151 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
152 } | |
153 } | |
154 | |
155 post_handler(r); | |
156 | |
157 return NGX_OK; | |
158 } | |
159 | |
160 /* | |
161 * to not consider the body as pipelined request in | |
162 * ngx_http_set_keepalive() | |
163 */ | |
164 r->header_in->pos = r->header_in->last; | |
165 | |
166 r->request_length += preread; | |
167 | |
168 rb->rest = r->headers_in.content_length_n - preread; | |
169 | |
170 if (rb->rest <= (off_t) (b->end - b->last)) { | |
171 | |
172 /* the whole request body may be placed in r->header_in */ | 104 /* the whole request body may be placed in r->header_in */ |
173 | 105 |
174 rb->to_write = rb->bufs; | 106 rb->buf = r->header_in; |
175 | |
176 r->read_event_handler = ngx_http_read_client_request_body_handler; | 107 r->read_event_handler = ngx_http_read_client_request_body_handler; |
177 | 108 r->write_event_handler = ngx_http_request_empty_handler; |
178 return ngx_http_do_read_client_request_body(r); | 109 |
179 } | 110 rc = ngx_http_do_read_client_request_body(r); |
180 | 111 goto done; |
181 next = &rb->bufs->next; | 112 } |
182 | 113 |
183 } else { | 114 } else { |
184 b = NULL; | 115 /* set rb->rest */ |
185 rb->rest = r->headers_in.content_length_n; | 116 |
186 next = &rb->bufs; | 117 if (ngx_http_request_body_filter(r, NULL) != NGX_OK) { |
187 } | 118 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; |
119 goto done; | |
120 } | |
121 } | |
122 | |
123 if (rb->rest == 0) { | |
124 /* the whole request body was pre-read */ | |
125 | |
126 if (r->request_body_in_file_only) { | |
127 if (ngx_http_write_request_body(r) != NGX_OK) { | |
128 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | |
129 goto done; | |
130 } | |
131 } | |
132 | |
133 post_handler(r); | |
134 | |
135 return NGX_OK; | |
136 } | |
137 | |
138 if (rb->rest < 0) { | |
139 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
140 "negative request body rest"); | |
141 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | |
142 goto done; | |
143 } | |
144 | |
145 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
188 | 146 |
189 size = clcf->client_body_buffer_size; | 147 size = clcf->client_body_buffer_size; |
190 size += size >> 2; | 148 size += size >> 2; |
191 | 149 |
192 if (rb->rest < size) { | 150 /* TODO: honor r->request_body_in_single_buf */ |
151 | |
152 if (!r->headers_in.chunked && rb->rest < size) { | |
193 size = (ssize_t) rb->rest; | 153 size = (ssize_t) rb->rest; |
194 | 154 |
195 if (r->request_body_in_single_buf) { | 155 if (r->request_body_in_single_buf) { |
196 size += preread; | 156 size += preread; |
197 } | 157 } |
198 | 158 |
199 } else { | 159 } else { |
200 size = clcf->client_body_buffer_size; | 160 size = clcf->client_body_buffer_size; |
201 | |
202 /* disable copying buffer for r->request_body_in_single_buf */ | |
203 b = NULL; | |
204 } | 161 } |
205 | 162 |
206 rb->buf = ngx_create_temp_buf(r->pool, size); | 163 rb->buf = ngx_create_temp_buf(r->pool, size); |
207 if (rb->buf == NULL) { | 164 if (rb->buf == NULL) { |
208 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 165 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; |
209 } | 166 goto done; |
210 | |
211 cl = ngx_alloc_chain_link(r->pool); | |
212 if (cl == NULL) { | |
213 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
214 } | |
215 | |
216 cl->buf = rb->buf; | |
217 cl->next = NULL; | |
218 | |
219 if (b && r->request_body_in_single_buf) { | |
220 size = b->last - b->pos; | |
221 ngx_memcpy(rb->buf->pos, b->pos, size); | |
222 rb->buf->last += size; | |
223 | |
224 next = &rb->bufs; | |
225 } | |
226 | |
227 *next = cl; | |
228 | |
229 if (r->request_body_in_file_only || r->request_body_in_single_buf) { | |
230 rb->to_write = rb->bufs; | |
231 | |
232 } else { | |
233 rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; | |
234 } | 167 } |
235 | 168 |
236 r->read_event_handler = ngx_http_read_client_request_body_handler; | 169 r->read_event_handler = ngx_http_read_client_request_body_handler; |
237 | 170 r->write_event_handler = ngx_http_request_empty_handler; |
238 return ngx_http_do_read_client_request_body(r); | 171 |
172 rc = ngx_http_do_read_client_request_body(r); | |
173 | |
174 done: | |
175 | |
176 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { | |
177 r->main->count--; | |
178 } | |
179 | |
180 return rc; | |
239 } | 181 } |
240 | 182 |
241 | 183 |
242 static void | 184 static void |
243 ngx_http_read_client_request_body_handler(ngx_http_request_t *r) | 185 ngx_http_read_client_request_body_handler(ngx_http_request_t *r) |
259 | 201 |
260 | 202 |
261 static ngx_int_t | 203 static ngx_int_t |
262 ngx_http_do_read_client_request_body(ngx_http_request_t *r) | 204 ngx_http_do_read_client_request_body(ngx_http_request_t *r) |
263 { | 205 { |
206 off_t rest; | |
264 size_t size; | 207 size_t size; |
265 ssize_t n; | 208 ssize_t n; |
209 ngx_int_t rc; | |
266 ngx_buf_t *b; | 210 ngx_buf_t *b; |
211 ngx_chain_t *cl, out; | |
267 ngx_connection_t *c; | 212 ngx_connection_t *c; |
268 ngx_http_request_body_t *rb; | 213 ngx_http_request_body_t *rb; |
269 ngx_http_core_loc_conf_t *clcf; | 214 ngx_http_core_loc_conf_t *clcf; |
270 | 215 |
271 c = r->connection; | 216 c = r->connection; |
276 | 221 |
277 for ( ;; ) { | 222 for ( ;; ) { |
278 for ( ;; ) { | 223 for ( ;; ) { |
279 if (rb->buf->last == rb->buf->end) { | 224 if (rb->buf->last == rb->buf->end) { |
280 | 225 |
281 if (ngx_http_write_request_body(r, rb->to_write) != NGX_OK) { | 226 /* pass buffer to request body filter chain */ |
227 | |
228 out.buf = rb->buf; | |
229 out.next = NULL; | |
230 | |
231 rc = ngx_http_request_body_filter(r, &out); | |
232 | |
233 if (rc != NGX_OK) { | |
234 return rc; | |
235 } | |
236 | |
237 /* write to file */ | |
238 | |
239 if (ngx_http_write_request_body(r) != NGX_OK) { | |
282 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 240 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
283 } | 241 } |
284 | 242 |
285 rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; | 243 /* update chains */ |
244 | |
245 rc = ngx_http_request_body_filter(r, NULL); | |
246 | |
247 if (rc != NGX_OK) { | |
248 return rc; | |
249 } | |
250 | |
251 if (rb->busy != NULL) { | |
252 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
253 } | |
254 | |
255 rb->buf->pos = rb->buf->start; | |
286 rb->buf->last = rb->buf->start; | 256 rb->buf->last = rb->buf->start; |
287 } | 257 } |
288 | 258 |
289 size = rb->buf->end - rb->buf->last; | 259 size = rb->buf->end - rb->buf->last; |
290 | 260 rest = rb->rest - (rb->buf->last - rb->buf->pos); |
291 if ((off_t) size > rb->rest) { | 261 |
292 size = (size_t) rb->rest; | 262 if ((off_t) size > rest) { |
263 size = (size_t) rest; | |
293 } | 264 } |
294 | 265 |
295 n = c->recv(c, rb->buf->last, size); | 266 n = c->recv(c, rb->buf->last, size); |
296 | 267 |
297 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | 268 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, |
310 c->error = 1; | 281 c->error = 1; |
311 return NGX_HTTP_BAD_REQUEST; | 282 return NGX_HTTP_BAD_REQUEST; |
312 } | 283 } |
313 | 284 |
314 rb->buf->last += n; | 285 rb->buf->last += n; |
315 rb->rest -= n; | |
316 r->request_length += n; | 286 r->request_length += n; |
287 | |
288 if (n == rest) { | |
289 /* pass buffer to request body filter chain */ | |
290 | |
291 out.buf = rb->buf; | |
292 out.next = NULL; | |
293 | |
294 rc = ngx_http_request_body_filter(r, &out); | |
295 | |
296 if (rc != NGX_OK) { | |
297 return rc; | |
298 } | |
299 } | |
317 | 300 |
318 if (rb->rest == 0) { | 301 if (rb->rest == 0) { |
319 break; | 302 break; |
320 } | 303 } |
321 | 304 |
349 | 332 |
350 if (rb->temp_file || r->request_body_in_file_only) { | 333 if (rb->temp_file || r->request_body_in_file_only) { |
351 | 334 |
352 /* save the last part */ | 335 /* save the last part */ |
353 | 336 |
354 if (ngx_http_write_request_body(r, rb->to_write) != NGX_OK) { | 337 if (ngx_http_write_request_body(r) != NGX_OK) { |
355 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 338 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
356 } | 339 } |
357 | 340 |
358 b = ngx_calloc_buf(r->pool); | 341 cl = ngx_chain_get_free_buf(r->pool, &rb->free); |
359 if (b == NULL) { | 342 if (cl == NULL) { |
360 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 343 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
361 } | 344 } |
362 | 345 |
346 b = cl->buf; | |
347 | |
348 ngx_memzero(b, sizeof(ngx_buf_t)); | |
349 | |
363 b->in_file = 1; | 350 b->in_file = 1; |
364 b->file_pos = 0; | |
365 b->file_last = rb->temp_file->file.offset; | 351 b->file_last = rb->temp_file->file.offset; |
366 b->file = &rb->temp_file->file; | 352 b->file = &rb->temp_file->file; |
367 | 353 |
368 if (rb->bufs->next) { | 354 rb->bufs = cl; |
369 rb->bufs->next->buf = b; | |
370 | |
371 } else { | |
372 rb->bufs->buf = b; | |
373 } | |
374 } | |
375 | |
376 if (rb->bufs->next | |
377 && (r->request_body_in_file_only || r->request_body_in_single_buf)) | |
378 { | |
379 rb->bufs = rb->bufs->next; | |
380 } | 355 } |
381 | 356 |
382 r->read_event_handler = ngx_http_block_reading; | 357 r->read_event_handler = ngx_http_block_reading; |
383 | 358 |
384 rb->post_handler(r); | 359 rb->post_handler(r); |
386 return NGX_OK; | 361 return NGX_OK; |
387 } | 362 } |
388 | 363 |
389 | 364 |
390 static ngx_int_t | 365 static ngx_int_t |
391 ngx_http_write_request_body(ngx_http_request_t *r, ngx_chain_t *body) | 366 ngx_http_write_request_body(ngx_http_request_t *r) |
392 { | 367 { |
393 ssize_t n; | 368 ssize_t n; |
369 ngx_chain_t *cl; | |
394 ngx_temp_file_t *tf; | 370 ngx_temp_file_t *tf; |
395 ngx_http_request_body_t *rb; | 371 ngx_http_request_body_t *rb; |
396 ngx_http_core_loc_conf_t *clcf; | 372 ngx_http_core_loc_conf_t *clcf; |
397 | 373 |
398 rb = r->request_body; | 374 rb = r->request_body; |
375 | |
376 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
377 "http write client request body, bufs %p", rb->bufs); | |
399 | 378 |
400 if (rb->temp_file == NULL) { | 379 if (rb->temp_file == NULL) { |
401 tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); | 380 tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); |
402 if (tf == NULL) { | 381 if (tf == NULL) { |
403 return NGX_ERROR; | 382 return NGX_ERROR; |
417 if (r->request_body_file_group_access) { | 396 if (r->request_body_file_group_access) { |
418 tf->access = 0660; | 397 tf->access = 0660; |
419 } | 398 } |
420 | 399 |
421 rb->temp_file = tf; | 400 rb->temp_file = tf; |
422 } | 401 |
423 | 402 if (rb->bufs == NULL) { |
424 n = ngx_write_chain_to_temp_file(rb->temp_file, body); | 403 /* empty body with r->request_body_in_file_only */ |
404 | |
405 if (ngx_create_temp_file(&tf->file, tf->path, tf->pool, | |
406 tf->persistent, tf->clean, tf->access) | |
407 != NGX_OK) | |
408 { | |
409 return NGX_ERROR; | |
410 } | |
411 | |
412 return NGX_OK; | |
413 } | |
414 } | |
415 | |
416 if (rb->bufs == NULL) { | |
417 return NGX_OK; | |
418 } | |
419 | |
420 n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs); | |
425 | 421 |
426 /* TODO: n == 0 or not complete and level event */ | 422 /* TODO: n == 0 or not complete and level event */ |
427 | 423 |
428 if (n == NGX_ERROR) { | 424 if (n == NGX_ERROR) { |
429 return NGX_ERROR; | 425 return NGX_ERROR; |
430 } | 426 } |
431 | 427 |
432 rb->temp_file->offset += n; | 428 rb->temp_file->offset += n; |
433 | 429 |
430 /* mark all buffers as written */ | |
431 | |
432 for (cl = rb->bufs; cl; cl = cl->next) { | |
433 cl->buf->pos = cl->buf->last; | |
434 } | |
435 | |
436 rb->bufs = NULL; | |
437 | |
434 return NGX_OK; | 438 return NGX_OK; |
435 } | 439 } |
436 | 440 |
437 | 441 |
438 ngx_int_t | 442 ngx_int_t |
439 ngx_http_discard_request_body(ngx_http_request_t *r) | 443 ngx_http_discard_request_body(ngx_http_request_t *r) |
440 { | 444 { |
441 ssize_t size; | 445 ssize_t size; |
446 ngx_int_t rc; | |
442 ngx_event_t *rev; | 447 ngx_event_t *rev; |
443 | 448 |
444 if (r != r->main || r->discard_body) { | 449 if (r != r->main || r->discard_body || r->request_body) { |
445 return NGX_OK; | 450 return NGX_OK; |
446 } | 451 } |
447 | 452 |
448 if (ngx_http_test_expect(r) != NGX_OK) { | 453 if (ngx_http_test_expect(r) != NGX_OK) { |
449 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 454 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
455 | 460 |
456 if (rev->timer_set) { | 461 if (rev->timer_set) { |
457 ngx_del_timer(rev); | 462 ngx_del_timer(rev); |
458 } | 463 } |
459 | 464 |
460 if (r->headers_in.content_length_n <= 0 || r->request_body) { | 465 if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) { |
461 return NGX_OK; | 466 return NGX_OK; |
462 } | 467 } |
463 | 468 |
464 size = r->header_in->last - r->header_in->pos; | 469 size = r->header_in->last - r->header_in->pos; |
465 | 470 |
466 if (size) { | 471 if (size || r->headers_in.chunked) { |
467 if (r->headers_in.content_length_n > size) { | 472 rc = ngx_http_discard_request_body_filter(r, r->header_in); |
468 r->header_in->pos += size; | 473 |
469 r->headers_in.content_length_n -= size; | 474 if (rc != NGX_OK) { |
470 | 475 return rc; |
471 } else { | 476 } |
472 r->header_in->pos += (size_t) r->headers_in.content_length_n; | 477 |
473 r->headers_in.content_length_n = 0; | 478 if (r->headers_in.content_length_n == 0) { |
474 return NGX_OK; | 479 return NGX_OK; |
475 } | 480 } |
476 } | 481 } |
482 | |
483 rc = ngx_http_read_discarded_request_body(r); | |
484 | |
485 if (rc == NGX_OK) { | |
486 r->lingering_close = 0; | |
487 return NGX_OK; | |
488 } | |
489 | |
490 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { | |
491 return rc; | |
492 } | |
493 | |
494 /* rc == NGX_AGAIN */ | |
477 | 495 |
478 r->read_event_handler = ngx_http_discarded_request_body_handler; | 496 r->read_event_handler = ngx_http_discarded_request_body_handler; |
479 | 497 |
480 if (ngx_handle_read_event(rev, 0) != NGX_OK) { | 498 if (ngx_handle_read_event(rev, 0) != NGX_OK) { |
481 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 499 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
482 } | 500 } |
483 | 501 |
484 if (ngx_http_read_discarded_request_body(r) == NGX_OK) { | 502 r->count++; |
485 r->lingering_close = 0; | 503 r->discard_body = 1; |
486 | |
487 } else { | |
488 r->count++; | |
489 r->discard_body = 1; | |
490 } | |
491 | 504 |
492 return NGX_OK; | 505 return NGX_OK; |
493 } | 506 } |
494 | 507 |
495 | 508 |
533 r->lingering_close = 0; | 546 r->lingering_close = 0; |
534 ngx_http_finalize_request(r, NGX_DONE); | 547 ngx_http_finalize_request(r, NGX_DONE); |
535 return; | 548 return; |
536 } | 549 } |
537 | 550 |
551 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { | |
552 c->error = 1; | |
553 ngx_http_finalize_request(r, NGX_ERROR); | |
554 return; | |
555 } | |
556 | |
538 /* rc == NGX_AGAIN */ | 557 /* rc == NGX_AGAIN */ |
539 | 558 |
540 if (ngx_handle_read_event(rev, 0) != NGX_OK) { | 559 if (ngx_handle_read_event(rev, 0) != NGX_OK) { |
541 c->error = 1; | 560 c->error = 1; |
542 ngx_http_finalize_request(r, NGX_ERROR); | 561 ngx_http_finalize_request(r, NGX_ERROR); |
559 | 578 |
560 | 579 |
561 static ngx_int_t | 580 static ngx_int_t |
562 ngx_http_read_discarded_request_body(ngx_http_request_t *r) | 581 ngx_http_read_discarded_request_body(ngx_http_request_t *r) |
563 { | 582 { |
564 size_t size; | 583 size_t size; |
565 ssize_t n; | 584 ssize_t n; |
566 u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE]; | 585 ngx_int_t rc; |
586 ngx_buf_t b; | |
587 u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE]; | |
567 | 588 |
568 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 589 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
569 "http read discarded body"); | 590 "http read discarded body"); |
591 | |
592 ngx_memzero(&b, sizeof(ngx_buf_t)); | |
593 | |
594 b.temporary = 1; | |
570 | 595 |
571 for ( ;; ) { | 596 for ( ;; ) { |
572 if (r->headers_in.content_length_n == 0) { | 597 if (r->headers_in.content_length_n == 0) { |
573 r->read_event_handler = ngx_http_block_reading; | 598 r->read_event_handler = ngx_http_block_reading; |
574 return NGX_OK; | 599 return NGX_OK; |
576 | 601 |
577 if (!r->connection->read->ready) { | 602 if (!r->connection->read->ready) { |
578 return NGX_AGAIN; | 603 return NGX_AGAIN; |
579 } | 604 } |
580 | 605 |
581 size = (r->headers_in.content_length_n > NGX_HTTP_DISCARD_BUFFER_SIZE) ? | 606 size = (size_t) ngx_min(r->headers_in.content_length_n, |
582 NGX_HTTP_DISCARD_BUFFER_SIZE: | 607 NGX_HTTP_DISCARD_BUFFER_SIZE); |
583 (size_t) r->headers_in.content_length_n; | |
584 | 608 |
585 n = r->connection->recv(r->connection, buffer, size); | 609 n = r->connection->recv(r->connection, buffer, size); |
586 | 610 |
587 if (n == NGX_ERROR) { | 611 if (n == NGX_ERROR) { |
588 r->connection->error = 1; | 612 r->connection->error = 1; |
595 | 619 |
596 if (n == 0) { | 620 if (n == 0) { |
597 return NGX_OK; | 621 return NGX_OK; |
598 } | 622 } |
599 | 623 |
600 r->headers_in.content_length_n -= n; | 624 b.pos = buffer; |
601 } | 625 b.last = buffer + n; |
626 | |
627 rc = ngx_http_discard_request_body_filter(r, &b); | |
628 | |
629 if (rc != NGX_OK) { | |
630 return rc; | |
631 } | |
632 } | |
633 } | |
634 | |
635 | |
636 static ngx_int_t | |
637 ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b) | |
638 { | |
639 size_t size; | |
640 ngx_int_t rc; | |
641 ngx_http_request_body_t *rb; | |
642 | |
643 if (r->headers_in.chunked) { | |
644 | |
645 rb = r->request_body; | |
646 | |
647 if (rb == NULL) { | |
648 | |
649 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); | |
650 if (rb == NULL) { | |
651 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
652 } | |
653 | |
654 rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t)); | |
655 if (rb->chunked == NULL) { | |
656 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
657 } | |
658 | |
659 r->request_body = rb; | |
660 } | |
661 | |
662 for ( ;; ) { | |
663 | |
664 rc = ngx_http_parse_chunked(r, b, rb->chunked); | |
665 | |
666 if (rc == NGX_OK) { | |
667 | |
668 /* a chunk has been parsed successfully */ | |
669 | |
670 size = b->last - b->pos; | |
671 | |
672 if ((off_t) size > rb->chunked->size) { | |
673 b->pos += rb->chunked->size; | |
674 rb->chunked->size = 0; | |
675 | |
676 } else { | |
677 rb->chunked->size -= size; | |
678 b->pos = b->last; | |
679 } | |
680 | |
681 continue; | |
682 } | |
683 | |
684 if (rc == NGX_DONE) { | |
685 | |
686 /* a whole response has been parsed successfully */ | |
687 | |
688 r->headers_in.content_length_n = 0; | |
689 break; | |
690 } | |
691 | |
692 if (rc == NGX_AGAIN) { | |
693 | |
694 /* set amount of data we want to see next time */ | |
695 | |
696 r->headers_in.content_length_n = rb->chunked->length; | |
697 break; | |
698 } | |
699 | |
700 /* invalid */ | |
701 | |
702 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
703 "client sent invalid chunked body"); | |
704 | |
705 return NGX_HTTP_BAD_REQUEST; | |
706 } | |
707 | |
708 } else { | |
709 size = b->last - b->pos; | |
710 | |
711 if ((off_t) size > r->headers_in.content_length_n) { | |
712 b->pos += r->headers_in.content_length_n; | |
713 r->headers_in.content_length_n = 0; | |
714 | |
715 } else { | |
716 b->pos = b->last; | |
717 r->headers_in.content_length_n -= size; | |
718 } | |
719 } | |
720 | |
721 return NGX_OK; | |
602 } | 722 } |
603 | 723 |
604 | 724 |
605 static ngx_int_t | 725 static ngx_int_t |
606 ngx_http_test_expect(ngx_http_request_t *r) | 726 ngx_http_test_expect(ngx_http_request_t *r) |
640 | 760 |
641 /* we assume that such small packet should be send successfully */ | 761 /* we assume that such small packet should be send successfully */ |
642 | 762 |
643 return NGX_ERROR; | 763 return NGX_ERROR; |
644 } | 764 } |
765 | |
766 | |
767 static ngx_int_t | |
768 ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) | |
769 { | |
770 if (r->headers_in.chunked) { | |
771 return ngx_http_request_body_chunked_filter(r, in); | |
772 | |
773 } else { | |
774 return ngx_http_request_body_length_filter(r, in); | |
775 } | |
776 } | |
777 | |
778 | |
779 static ngx_int_t | |
780 ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in) | |
781 { | |
782 size_t size; | |
783 ngx_int_t rc; | |
784 ngx_buf_t *b; | |
785 ngx_chain_t *cl, *tl, *out, **ll; | |
786 ngx_http_request_body_t *rb; | |
787 | |
788 rb = r->request_body; | |
789 | |
790 if (rb->rest == -1) { | |
791 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
792 "http request body content length filter"); | |
793 | |
794 rb->rest = r->headers_in.content_length_n; | |
795 } | |
796 | |
797 out = NULL; | |
798 ll = &out; | |
799 | |
800 for (cl = in; cl; cl = cl->next) { | |
801 | |
802 tl = ngx_chain_get_free_buf(r->pool, &rb->free); | |
803 if (tl == NULL) { | |
804 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
805 } | |
806 | |
807 b = tl->buf; | |
808 | |
809 ngx_memzero(b, sizeof(ngx_buf_t)); | |
810 | |
811 b->temporary = 1; | |
812 b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body; | |
813 b->start = cl->buf->start; | |
814 b->pos = cl->buf->pos; | |
815 b->last = cl->buf->last; | |
816 b->end = cl->buf->end; | |
817 | |
818 size = cl->buf->last - cl->buf->pos; | |
819 | |
820 if ((off_t) size < rb->rest) { | |
821 cl->buf->pos = cl->buf->last; | |
822 rb->rest -= size; | |
823 | |
824 } else { | |
825 cl->buf->pos += rb->rest; | |
826 rb->rest = 0; | |
827 b->last = cl->buf->pos; | |
828 b->last_buf = 1; | |
829 } | |
830 | |
831 *ll = tl; | |
832 ll = &tl->next; | |
833 } | |
834 | |
835 rc = ngx_http_request_body_save_filter(r, out); | |
836 | |
837 ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out, | |
838 (ngx_buf_tag_t) &ngx_http_read_client_request_body); | |
839 | |
840 return rc; | |
841 } | |
842 | |
843 | |
844 static ngx_int_t | |
845 ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) | |
846 { | |
847 size_t size; | |
848 ngx_int_t rc; | |
849 ngx_buf_t *b; | |
850 ngx_chain_t *cl, *out, *tl, **ll; | |
851 ngx_http_request_body_t *rb; | |
852 ngx_http_core_loc_conf_t *clcf; | |
853 | |
854 rb = r->request_body; | |
855 | |
856 if (rb->rest == -1) { | |
857 | |
858 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
859 "http request body chunked filter"); | |
860 | |
861 rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t)); | |
862 if (rb->chunked == NULL) { | |
863 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
864 } | |
865 | |
866 r->headers_in.content_length_n = 0; | |
867 rb->rest = 3; | |
868 } | |
869 | |
870 out = NULL; | |
871 ll = &out; | |
872 | |
873 for (cl = in; cl; cl = cl->next) { | |
874 | |
875 for ( ;; ) { | |
876 | |
877 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, | |
878 "http body chunked buf " | |
879 "t:%d f:%d %p, pos %p, size: %z file: %O, size: %z", | |
880 cl->buf->temporary, cl->buf->in_file, | |
881 cl->buf->start, cl->buf->pos, | |
882 cl->buf->last - cl->buf->pos, | |
883 cl->buf->file_pos, | |
884 cl->buf->file_last - cl->buf->file_pos); | |
885 | |
886 rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked); | |
887 | |
888 if (rc == NGX_OK) { | |
889 | |
890 /* a chunk has been parsed successfully */ | |
891 | |
892 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
893 | |
894 if (clcf->client_max_body_size | |
895 && clcf->client_max_body_size | |
896 < r->headers_in.content_length_n + rb->chunked->size) | |
897 { | |
898 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
899 "client intended to send too large chunked " | |
900 "body: %O bytes", | |
901 r->headers_in.content_length_n | |
902 + rb->chunked->size); | |
903 | |
904 r->lingering_close = 1; | |
905 | |
906 return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; | |
907 } | |
908 | |
909 tl = ngx_chain_get_free_buf(r->pool, &rb->free); | |
910 if (tl == NULL) { | |
911 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
912 } | |
913 | |
914 b = tl->buf; | |
915 | |
916 ngx_memzero(b, sizeof(ngx_buf_t)); | |
917 | |
918 b->temporary = 1; | |
919 b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body; | |
920 b->start = cl->buf->start; | |
921 b->pos = cl->buf->pos; | |
922 b->last = cl->buf->last; | |
923 b->end = cl->buf->end; | |
924 | |
925 *ll = tl; | |
926 ll = &tl->next; | |
927 | |
928 size = cl->buf->last - cl->buf->pos; | |
929 | |
930 if ((off_t) size > rb->chunked->size) { | |
931 cl->buf->pos += rb->chunked->size; | |
932 r->headers_in.content_length_n += rb->chunked->size; | |
933 rb->chunked->size = 0; | |
934 | |
935 } else { | |
936 rb->chunked->size -= size; | |
937 r->headers_in.content_length_n += size; | |
938 cl->buf->pos = cl->buf->last; | |
939 } | |
940 | |
941 b->last = cl->buf->pos; | |
942 | |
943 continue; | |
944 } | |
945 | |
946 if (rc == NGX_DONE) { | |
947 | |
948 /* a whole response has been parsed successfully */ | |
949 | |
950 rb->rest = 0; | |
951 | |
952 tl = ngx_chain_get_free_buf(r->pool, &rb->free); | |
953 if (tl == NULL) { | |
954 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
955 } | |
956 | |
957 b = tl->buf; | |
958 | |
959 ngx_memzero(b, sizeof(ngx_buf_t)); | |
960 | |
961 b->last_buf = 1; | |
962 | |
963 *ll = tl; | |
964 ll = &tl->next; | |
965 | |
966 break; | |
967 } | |
968 | |
969 if (rc == NGX_AGAIN) { | |
970 | |
971 /* set rb->rest, amount of data we want to see next time */ | |
972 | |
973 rb->rest = rb->chunked->length; | |
974 | |
975 break; | |
976 } | |
977 | |
978 /* invalid */ | |
979 | |
980 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
981 "client sent invalid chunked body"); | |
982 | |
983 return NGX_HTTP_BAD_REQUEST; | |
984 } | |
985 } | |
986 | |
987 rc = ngx_http_request_body_save_filter(r, out); | |
988 | |
989 ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out, | |
990 (ngx_buf_tag_t) &ngx_http_read_client_request_body); | |
991 | |
992 return rc; | |
993 } | |
994 | |
995 | |
996 static ngx_int_t | |
997 ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in) | |
998 { | |
999 #if (NGX_DEBUG) | |
1000 ngx_chain_t *cl; | |
1001 #endif | |
1002 ngx_http_request_body_t *rb; | |
1003 | |
1004 rb = r->request_body; | |
1005 | |
1006 #if (NGX_DEBUG) | |
1007 | |
1008 for (cl = rb->bufs; cl; cl = cl->next) { | |
1009 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, | |
1010 "http body old buf t:%d f:%d %p, pos %p, size: %z " | |
1011 "file: %O, size: %z", | |
1012 cl->buf->temporary, cl->buf->in_file, | |
1013 cl->buf->start, cl->buf->pos, | |
1014 cl->buf->last - cl->buf->pos, | |
1015 cl->buf->file_pos, | |
1016 cl->buf->file_last - cl->buf->file_pos); | |
1017 } | |
1018 | |
1019 for (cl = in; cl; cl = cl->next) { | |
1020 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, | |
1021 "http body new buf t:%d f:%d %p, pos %p, size: %z " | |
1022 "file: %O, size: %z", | |
1023 cl->buf->temporary, cl->buf->in_file, | |
1024 cl->buf->start, cl->buf->pos, | |
1025 cl->buf->last - cl->buf->pos, | |
1026 cl->buf->file_pos, | |
1027 cl->buf->file_last - cl->buf->file_pos); | |
1028 } | |
1029 | |
1030 #endif | |
1031 | |
1032 /* TODO: coalesce neighbouring buffers */ | |
1033 | |
1034 if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) { | |
1035 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
1036 } | |
1037 | |
1038 return NGX_OK; | |
1039 } |