Mercurial > hg > nginx
comparison src/http/ngx_http_request_body.c @ 4930:6f085bfcdb4d
Request body: chunked transfer encoding support.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Wed, 21 Nov 2012 01:08:11 +0000 |
parents | caca5603bded |
children | 190fce68b6b5 |
comparison
equal
deleted
inserted
replaced
4929:5a44d638cd27 | 4930:6f085bfcdb4d |
---|---|
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_int_t rc; | 37 ngx_int_t rc; |
35 ngx_buf_t *b; | 38 ngx_chain_t out; |
36 ngx_chain_t *cl, **next; | |
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 |
52 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); | 54 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); |
53 if (rb == NULL) { | 55 if (rb == NULL) { |
54 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | 56 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; |
55 goto done; | 57 goto done; |
56 } | 58 } |
57 | |
58 r->request_body = rb; | |
59 | |
60 if (r->headers_in.content_length_n < 0) { | |
61 post_handler(r); | |
62 return NGX_OK; | |
63 } | |
64 | |
65 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
66 | |
67 if (r->headers_in.content_length_n == 0) { | |
68 | |
69 if (r->request_body_in_file_only) { | |
70 if (ngx_http_write_request_body(r, NULL) != NGX_OK) { | |
71 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | |
72 goto done; | |
73 } | |
74 } | |
75 | |
76 post_handler(r); | |
77 | |
78 return NGX_OK; | |
79 } | |
80 | |
81 rb->post_handler = post_handler; | |
82 | 59 |
83 /* | 60 /* |
84 * set by ngx_pcalloc(): | 61 * set by ngx_pcalloc(): |
85 * | 62 * |
86 * rb->bufs = NULL; | 63 * rb->bufs = NULL; |
87 * rb->buf = NULL; | 64 * rb->buf = NULL; |
88 * rb->rest = 0; | 65 * rb->free = NULL; |
66 * rb->busy = NULL; | |
67 * rb->chunked = NULL; | |
89 */ | 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 } | |
90 | 79 |
91 preread = r->header_in->last - r->header_in->pos; | 80 preread = r->header_in->last - r->header_in->pos; |
92 | 81 |
93 if (preread) { | 82 if (preread) { |
94 | 83 |
95 /* there is the pre-read part of the request body */ | 84 /* there is the pre-read part of the request body */ |
96 | 85 |
97 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 86 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
98 "http client request body preread %uz", preread); | 87 "http client request body preread %uz", preread); |
99 | 88 |
100 b = ngx_calloc_buf(r->pool); | 89 out.buf = r->header_in; |
101 if (b == NULL) { | 90 out.next = NULL; |
91 | |
92 rc = ngx_http_request_body_filter(r, &out); | |
93 | |
94 if (rc != NGX_OK) { | |
95 goto done; | |
96 } | |
97 | |
98 r->request_length += preread - (r->header_in->last - r->header_in->pos); | |
99 | |
100 if (!r->headers_in.chunked | |
101 && rb->rest > 0 | |
102 && rb->rest <= (off_t) (r->header_in->end - r->header_in->last)) | |
103 { | |
104 /* the whole request body may be placed in r->header_in */ | |
105 | |
106 rb->buf = r->header_in; | |
107 r->read_event_handler = ngx_http_read_client_request_body_handler; | |
108 | |
109 rc = ngx_http_do_read_client_request_body(r); | |
110 goto done; | |
111 } | |
112 | |
113 } else { | |
114 /* set rb->rest */ | |
115 | |
116 if (ngx_http_request_body_filter(r, NULL) != NGX_OK) { | |
102 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | 117 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; |
103 goto done; | 118 goto done; |
104 } | 119 } |
105 | 120 } |
106 b->temporary = 1; | 121 |
107 b->start = r->header_in->pos; | 122 if (rb->rest == 0) { |
108 b->pos = r->header_in->pos; | 123 /* the whole request body was pre-read */ |
109 b->last = r->header_in->last; | 124 |
110 b->end = r->header_in->end; | 125 if (r->request_body_in_file_only) { |
111 | 126 if (ngx_http_write_request_body(r) != NGX_OK) { |
112 rb->bufs = ngx_alloc_chain_link(r->pool); | 127 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; |
113 if (rb->bufs == NULL) { | 128 goto done; |
114 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | 129 } |
115 goto done; | 130 } |
116 } | 131 |
117 | 132 post_handler(r); |
118 rb->bufs->buf = b; | 133 |
119 rb->bufs->next = NULL; | 134 return NGX_OK; |
120 | 135 } |
121 rb->buf = b; | 136 |
122 | 137 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
123 if ((off_t) preread >= r->headers_in.content_length_n) { | |
124 | |
125 /* the whole request body was pre-read */ | |
126 | |
127 r->header_in->pos += (size_t) r->headers_in.content_length_n; | |
128 r->request_length += r->headers_in.content_length_n; | |
129 b->last = r->header_in->pos; | |
130 | |
131 if (r->request_body_in_file_only) { | |
132 if (ngx_http_write_request_body(r, rb->bufs) != NGX_OK) { | |
133 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | |
134 goto done; | |
135 } | |
136 } | |
137 | |
138 post_handler(r); | |
139 | |
140 return NGX_OK; | |
141 } | |
142 | |
143 /* | |
144 * to not consider the body as pipelined request in | |
145 * ngx_http_set_keepalive() | |
146 */ | |
147 r->header_in->pos = r->header_in->last; | |
148 | |
149 r->request_length += preread; | |
150 | |
151 rb->rest = r->headers_in.content_length_n - preread; | |
152 | |
153 if (rb->rest <= (off_t) (b->end - b->last)) { | |
154 | |
155 /* the whole request body may be placed in r->header_in */ | |
156 | |
157 rb->to_write = rb->bufs; | |
158 | |
159 r->read_event_handler = ngx_http_read_client_request_body_handler; | |
160 | |
161 rc = ngx_http_do_read_client_request_body(r); | |
162 goto done; | |
163 } | |
164 | |
165 next = &rb->bufs->next; | |
166 | |
167 } else { | |
168 b = NULL; | |
169 rb->rest = r->headers_in.content_length_n; | |
170 next = &rb->bufs; | |
171 } | |
172 | 138 |
173 size = clcf->client_body_buffer_size; | 139 size = clcf->client_body_buffer_size; |
174 size += size >> 2; | 140 size += size >> 2; |
175 | 141 |
176 if (rb->rest < size) { | 142 /* TODO: honor r->request_body_in_single_buf */ |
143 | |
144 if (!r->headers_in.chunked && rb->rest < size) { | |
177 size = (ssize_t) rb->rest; | 145 size = (ssize_t) rb->rest; |
178 | 146 |
179 if (r->request_body_in_single_buf) { | 147 if (r->request_body_in_single_buf) { |
180 size += preread; | 148 size += preread; |
181 } | 149 } |
182 | 150 |
183 } else { | 151 } else { |
184 size = clcf->client_body_buffer_size; | 152 size = clcf->client_body_buffer_size; |
185 | |
186 /* disable copying buffer for r->request_body_in_single_buf */ | |
187 b = NULL; | |
188 } | 153 } |
189 | 154 |
190 rb->buf = ngx_create_temp_buf(r->pool, size); | 155 rb->buf = ngx_create_temp_buf(r->pool, size); |
191 if (rb->buf == NULL) { | 156 if (rb->buf == NULL) { |
192 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | 157 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; |
193 goto done; | 158 goto done; |
194 } | 159 } |
195 | 160 |
196 cl = ngx_alloc_chain_link(r->pool); | |
197 if (cl == NULL) { | |
198 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | |
199 goto done; | |
200 } | |
201 | |
202 cl->buf = rb->buf; | |
203 cl->next = NULL; | |
204 | |
205 if (b && r->request_body_in_single_buf) { | |
206 size = b->last - b->pos; | |
207 ngx_memcpy(rb->buf->pos, b->pos, size); | |
208 rb->buf->last += size; | |
209 | |
210 next = &rb->bufs; | |
211 } | |
212 | |
213 *next = cl; | |
214 | |
215 if (r->request_body_in_file_only || r->request_body_in_single_buf) { | |
216 rb->to_write = rb->bufs; | |
217 | |
218 } else { | |
219 rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; | |
220 } | |
221 | |
222 r->read_event_handler = ngx_http_read_client_request_body_handler; | 161 r->read_event_handler = ngx_http_read_client_request_body_handler; |
223 | 162 |
224 rc = ngx_http_do_read_client_request_body(r); | 163 rc = ngx_http_do_read_client_request_body(r); |
225 | 164 |
226 done: | 165 done: |
253 | 192 |
254 | 193 |
255 static ngx_int_t | 194 static ngx_int_t |
256 ngx_http_do_read_client_request_body(ngx_http_request_t *r) | 195 ngx_http_do_read_client_request_body(ngx_http_request_t *r) |
257 { | 196 { |
197 off_t rest; | |
258 size_t size; | 198 size_t size; |
259 ssize_t n; | 199 ssize_t n; |
200 ngx_int_t rc; | |
260 ngx_buf_t *b; | 201 ngx_buf_t *b; |
202 ngx_chain_t *cl, out; | |
261 ngx_connection_t *c; | 203 ngx_connection_t *c; |
262 ngx_http_request_body_t *rb; | 204 ngx_http_request_body_t *rb; |
263 ngx_http_core_loc_conf_t *clcf; | 205 ngx_http_core_loc_conf_t *clcf; |
264 | 206 |
265 c = r->connection; | 207 c = r->connection; |
270 | 212 |
271 for ( ;; ) { | 213 for ( ;; ) { |
272 for ( ;; ) { | 214 for ( ;; ) { |
273 if (rb->buf->last == rb->buf->end) { | 215 if (rb->buf->last == rb->buf->end) { |
274 | 216 |
275 if (ngx_http_write_request_body(r, rb->to_write) != NGX_OK) { | 217 /* pass buffer to request body filter chain */ |
218 | |
219 out.buf = rb->buf; | |
220 out.next = NULL; | |
221 | |
222 rc = ngx_http_request_body_filter(r, &out); | |
223 | |
224 if (rc != NGX_OK) { | |
225 return rc; | |
226 } | |
227 | |
228 /* write to file */ | |
229 | |
230 if (ngx_http_write_request_body(r) != NGX_OK) { | |
276 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 231 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
277 } | 232 } |
278 | 233 |
279 rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; | 234 /* update chains */ |
235 | |
236 rc = ngx_http_request_body_filter(r, NULL); | |
237 | |
238 if (rc != NGX_OK) { | |
239 return rc; | |
240 } | |
241 | |
242 if (rb->busy != NULL) { | |
243 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
244 } | |
245 | |
246 rb->buf->pos = rb->buf->start; | |
280 rb->buf->last = rb->buf->start; | 247 rb->buf->last = rb->buf->start; |
281 } | 248 } |
282 | 249 |
283 size = rb->buf->end - rb->buf->last; | 250 size = rb->buf->end - rb->buf->last; |
284 | 251 rest = rb->rest - (rb->buf->last - rb->buf->pos); |
285 if ((off_t) size > rb->rest) { | 252 |
286 size = (size_t) rb->rest; | 253 if ((off_t) size > rest) { |
254 size = (size_t) rest; | |
287 } | 255 } |
288 | 256 |
289 n = c->recv(c, rb->buf->last, size); | 257 n = c->recv(c, rb->buf->last, size); |
290 | 258 |
291 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | 259 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, |
304 c->error = 1; | 272 c->error = 1; |
305 return NGX_HTTP_BAD_REQUEST; | 273 return NGX_HTTP_BAD_REQUEST; |
306 } | 274 } |
307 | 275 |
308 rb->buf->last += n; | 276 rb->buf->last += n; |
309 rb->rest -= n; | |
310 r->request_length += n; | 277 r->request_length += n; |
278 | |
279 if (n == rest) { | |
280 /* pass buffer to request body filter chain */ | |
281 | |
282 out.buf = rb->buf; | |
283 out.next = NULL; | |
284 | |
285 rc = ngx_http_request_body_filter(r, &out); | |
286 | |
287 if (rc != NGX_OK) { | |
288 return rc; | |
289 } | |
290 } | |
311 | 291 |
312 if (rb->rest == 0) { | 292 if (rb->rest == 0) { |
313 break; | 293 break; |
314 } | 294 } |
315 | 295 |
343 | 323 |
344 if (rb->temp_file || r->request_body_in_file_only) { | 324 if (rb->temp_file || r->request_body_in_file_only) { |
345 | 325 |
346 /* save the last part */ | 326 /* save the last part */ |
347 | 327 |
348 if (ngx_http_write_request_body(r, rb->to_write) != NGX_OK) { | 328 if (ngx_http_write_request_body(r) != NGX_OK) { |
349 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 329 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
350 } | 330 } |
351 | 331 |
352 b = ngx_calloc_buf(r->pool); | 332 cl = ngx_chain_get_free_buf(r->pool, &rb->free); |
353 if (b == NULL) { | 333 if (cl == NULL) { |
354 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 334 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
355 } | 335 } |
356 | 336 |
337 b = cl->buf; | |
338 | |
339 ngx_memzero(b, sizeof(ngx_buf_t)); | |
340 | |
357 b->in_file = 1; | 341 b->in_file = 1; |
358 b->file_pos = 0; | |
359 b->file_last = rb->temp_file->file.offset; | 342 b->file_last = rb->temp_file->file.offset; |
360 b->file = &rb->temp_file->file; | 343 b->file = &rb->temp_file->file; |
361 | 344 |
362 if (rb->bufs->next) { | 345 rb->bufs = cl; |
363 rb->bufs->next->buf = b; | |
364 | |
365 } else { | |
366 rb->bufs->buf = b; | |
367 } | |
368 } | |
369 | |
370 if (rb->bufs->next | |
371 && (r->request_body_in_file_only || r->request_body_in_single_buf)) | |
372 { | |
373 rb->bufs = rb->bufs->next; | |
374 } | 346 } |
375 | 347 |
376 r->read_event_handler = ngx_http_block_reading; | 348 r->read_event_handler = ngx_http_block_reading; |
377 | 349 |
378 rb->post_handler(r); | 350 rb->post_handler(r); |
380 return NGX_OK; | 352 return NGX_OK; |
381 } | 353 } |
382 | 354 |
383 | 355 |
384 static ngx_int_t | 356 static ngx_int_t |
385 ngx_http_write_request_body(ngx_http_request_t *r, ngx_chain_t *body) | 357 ngx_http_write_request_body(ngx_http_request_t *r) |
386 { | 358 { |
387 ssize_t n; | 359 ssize_t n; |
360 ngx_chain_t *cl; | |
388 ngx_temp_file_t *tf; | 361 ngx_temp_file_t *tf; |
389 ngx_http_request_body_t *rb; | 362 ngx_http_request_body_t *rb; |
390 ngx_http_core_loc_conf_t *clcf; | 363 ngx_http_core_loc_conf_t *clcf; |
391 | 364 |
392 rb = r->request_body; | 365 rb = r->request_body; |
366 | |
367 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
368 "http write client request body, bufs %p", rb->bufs); | |
393 | 369 |
394 if (rb->temp_file == NULL) { | 370 if (rb->temp_file == NULL) { |
395 tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); | 371 tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); |
396 if (tf == NULL) { | 372 if (tf == NULL) { |
397 return NGX_ERROR; | 373 return NGX_ERROR; |
412 tf->access = 0660; | 388 tf->access = 0660; |
413 } | 389 } |
414 | 390 |
415 rb->temp_file = tf; | 391 rb->temp_file = tf; |
416 | 392 |
417 if (body == NULL) { | 393 if (rb->bufs == NULL) { |
418 /* empty body with r->request_body_in_file_only */ | 394 /* empty body with r->request_body_in_file_only */ |
419 | 395 |
420 if (ngx_create_temp_file(&tf->file, tf->path, tf->pool, | 396 if (ngx_create_temp_file(&tf->file, tf->path, tf->pool, |
421 tf->persistent, tf->clean, tf->access) | 397 tf->persistent, tf->clean, tf->access) |
422 != NGX_OK) | 398 != NGX_OK) |
426 | 402 |
427 return NGX_OK; | 403 return NGX_OK; |
428 } | 404 } |
429 } | 405 } |
430 | 406 |
431 n = ngx_write_chain_to_temp_file(rb->temp_file, body); | 407 if (rb->bufs == NULL) { |
408 return NGX_OK; | |
409 } | |
410 | |
411 n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs); | |
432 | 412 |
433 /* TODO: n == 0 or not complete and level event */ | 413 /* TODO: n == 0 or not complete and level event */ |
434 | 414 |
435 if (n == NGX_ERROR) { | 415 if (n == NGX_ERROR) { |
436 return NGX_ERROR; | 416 return NGX_ERROR; |
437 } | 417 } |
438 | 418 |
439 rb->temp_file->offset += n; | 419 rb->temp_file->offset += n; |
440 | 420 |
421 /* mark all buffers as written */ | |
422 | |
423 for (cl = rb->bufs; cl; cl = cl->next) { | |
424 cl->buf->pos = cl->buf->last; | |
425 } | |
426 | |
427 rb->bufs = NULL; | |
428 | |
441 return NGX_OK; | 429 return NGX_OK; |
442 } | 430 } |
443 | 431 |
444 | 432 |
445 ngx_int_t | 433 ngx_int_t |
446 ngx_http_discard_request_body(ngx_http_request_t *r) | 434 ngx_http_discard_request_body(ngx_http_request_t *r) |
447 { | 435 { |
448 ssize_t size; | 436 ssize_t size; |
437 ngx_int_t rc; | |
449 ngx_event_t *rev; | 438 ngx_event_t *rev; |
450 | 439 |
451 if (r != r->main || r->discard_body) { | 440 if (r != r->main || r->discard_body || r->request_body) { |
452 return NGX_OK; | 441 return NGX_OK; |
453 } | 442 } |
454 | 443 |
455 if (ngx_http_test_expect(r) != NGX_OK) { | 444 if (ngx_http_test_expect(r) != NGX_OK) { |
456 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 445 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
462 | 451 |
463 if (rev->timer_set) { | 452 if (rev->timer_set) { |
464 ngx_del_timer(rev); | 453 ngx_del_timer(rev); |
465 } | 454 } |
466 | 455 |
467 if (r->headers_in.content_length_n <= 0 || r->request_body) { | 456 if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) { |
468 return NGX_OK; | 457 return NGX_OK; |
469 } | 458 } |
470 | 459 |
471 size = r->header_in->last - r->header_in->pos; | 460 size = r->header_in->last - r->header_in->pos; |
472 | 461 |
473 if (size) { | 462 if (size) { |
474 if (r->headers_in.content_length_n > size) { | 463 rc = ngx_http_discard_request_body_filter(r, r->header_in); |
475 r->header_in->pos += size; | 464 |
476 r->headers_in.content_length_n -= size; | 465 if (rc != NGX_OK) { |
477 | 466 return rc; |
478 } else { | 467 } |
479 r->header_in->pos += (size_t) r->headers_in.content_length_n; | 468 |
480 r->headers_in.content_length_n = 0; | 469 if (r->headers_in.content_length_n == 0) { |
481 return NGX_OK; | 470 return NGX_OK; |
482 } | 471 } |
483 } | 472 } |
484 | 473 |
485 if (ngx_http_read_discarded_request_body(r) == NGX_OK) { | 474 if (ngx_http_read_discarded_request_body(r) == NGX_OK) { |
568 | 557 |
569 | 558 |
570 static ngx_int_t | 559 static ngx_int_t |
571 ngx_http_read_discarded_request_body(ngx_http_request_t *r) | 560 ngx_http_read_discarded_request_body(ngx_http_request_t *r) |
572 { | 561 { |
573 size_t size; | 562 size_t size; |
574 ssize_t n; | 563 ssize_t n; |
575 u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE]; | 564 ngx_int_t rc; |
565 ngx_buf_t b; | |
566 u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE]; | |
576 | 567 |
577 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 568 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
578 "http read discarded body"); | 569 "http read discarded body"); |
570 | |
571 ngx_memzero(&b, sizeof(ngx_buf_t)); | |
572 | |
573 b.temporary = 1; | |
579 | 574 |
580 for ( ;; ) { | 575 for ( ;; ) { |
581 if (r->headers_in.content_length_n == 0) { | 576 if (r->headers_in.content_length_n == 0) { |
582 r->read_event_handler = ngx_http_block_reading; | 577 r->read_event_handler = ngx_http_block_reading; |
583 return NGX_OK; | 578 return NGX_OK; |
585 | 580 |
586 if (!r->connection->read->ready) { | 581 if (!r->connection->read->ready) { |
587 return NGX_AGAIN; | 582 return NGX_AGAIN; |
588 } | 583 } |
589 | 584 |
590 size = (r->headers_in.content_length_n > NGX_HTTP_DISCARD_BUFFER_SIZE) ? | 585 size = (size_t) ngx_min(r->headers_in.content_length_n, |
591 NGX_HTTP_DISCARD_BUFFER_SIZE: | 586 NGX_HTTP_DISCARD_BUFFER_SIZE); |
592 (size_t) r->headers_in.content_length_n; | |
593 | 587 |
594 n = r->connection->recv(r->connection, buffer, size); | 588 n = r->connection->recv(r->connection, buffer, size); |
595 | 589 |
596 if (n == NGX_ERROR) { | 590 if (n == NGX_ERROR) { |
597 r->connection->error = 1; | 591 r->connection->error = 1; |
604 | 598 |
605 if (n == 0) { | 599 if (n == 0) { |
606 return NGX_OK; | 600 return NGX_OK; |
607 } | 601 } |
608 | 602 |
609 r->headers_in.content_length_n -= n; | 603 b.pos = buffer; |
610 } | 604 b.last = buffer + n; |
605 | |
606 rc = ngx_http_discard_request_body_filter(r, &b); | |
607 | |
608 if (rc != NGX_OK) { | |
609 r->connection->error = 1; | |
610 return NGX_OK; | |
611 } | |
612 } | |
613 } | |
614 | |
615 | |
616 static ngx_int_t | |
617 ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b) | |
618 { | |
619 size_t size; | |
620 ngx_int_t rc; | |
621 ngx_http_request_body_t *rb; | |
622 | |
623 if (r->headers_in.chunked) { | |
624 | |
625 rb = r->request_body; | |
626 | |
627 if (rb == NULL) { | |
628 | |
629 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); | |
630 if (rb == NULL) { | |
631 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
632 } | |
633 | |
634 rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t)); | |
635 if (rb == NULL) { | |
636 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
637 } | |
638 | |
639 r->request_body = rb; | |
640 } | |
641 | |
642 for ( ;; ) { | |
643 | |
644 rc = ngx_http_parse_chunked(r, b, rb->chunked); | |
645 | |
646 if (rc == NGX_OK) { | |
647 | |
648 /* a chunk has been parsed successfully */ | |
649 | |
650 size = b->last - b->pos; | |
651 | |
652 if ((off_t) size > rb->chunked->size) { | |
653 b->pos += rb->chunked->size; | |
654 rb->chunked->size = 0; | |
655 | |
656 } else { | |
657 rb->chunked->size -= size; | |
658 b->pos = b->last; | |
659 } | |
660 | |
661 continue; | |
662 } | |
663 | |
664 if (rc == NGX_DONE) { | |
665 | |
666 /* a whole response has been parsed successfully */ | |
667 | |
668 r->headers_in.content_length_n = 0; | |
669 break; | |
670 } | |
671 | |
672 if (rc == NGX_AGAIN) { | |
673 | |
674 /* set amount of data we want to see next time */ | |
675 | |
676 r->headers_in.content_length_n = rb->chunked->length; | |
677 break; | |
678 } | |
679 | |
680 /* invalid */ | |
681 | |
682 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
683 "client sent invalid chunked body"); | |
684 | |
685 return NGX_HTTP_BAD_REQUEST; | |
686 } | |
687 | |
688 } else { | |
689 size = b->last - b->pos; | |
690 | |
691 if ((off_t) size > r->headers_in.content_length_n) { | |
692 b->pos += r->headers_in.content_length_n; | |
693 r->headers_in.content_length_n = 0; | |
694 | |
695 } else { | |
696 b->pos = b->last; | |
697 r->headers_in.content_length_n -= size; | |
698 } | |
699 } | |
700 | |
701 return NGX_OK; | |
611 } | 702 } |
612 | 703 |
613 | 704 |
614 static ngx_int_t | 705 static ngx_int_t |
615 ngx_http_test_expect(ngx_http_request_t *r) | 706 ngx_http_test_expect(ngx_http_request_t *r) |
649 | 740 |
650 /* we assume that such small packet should be send successfully */ | 741 /* we assume that such small packet should be send successfully */ |
651 | 742 |
652 return NGX_ERROR; | 743 return NGX_ERROR; |
653 } | 744 } |
745 | |
746 | |
747 static ngx_int_t | |
748 ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) | |
749 { | |
750 if (r->headers_in.chunked) { | |
751 return ngx_http_request_body_chunked_filter(r, in); | |
752 | |
753 } else { | |
754 return ngx_http_request_body_length_filter(r, in); | |
755 } | |
756 } | |
757 | |
758 | |
759 static ngx_int_t | |
760 ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in) | |
761 { | |
762 size_t size; | |
763 ngx_int_t rc; | |
764 ngx_buf_t *b; | |
765 ngx_chain_t *cl, *tl, *out, **ll; | |
766 ngx_http_request_body_t *rb; | |
767 | |
768 rb = r->request_body; | |
769 | |
770 if (rb->rest == -1) { | |
771 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
772 "http request body content length filter"); | |
773 | |
774 rb->rest = r->headers_in.content_length_n; | |
775 } | |
776 | |
777 out = NULL; | |
778 ll = &out; | |
779 | |
780 for (cl = in; cl; cl = cl->next) { | |
781 | |
782 tl = ngx_chain_get_free_buf(r->pool, &rb->free); | |
783 if (tl == NULL) { | |
784 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
785 } | |
786 | |
787 b = tl->buf; | |
788 | |
789 ngx_memzero(b, sizeof(ngx_buf_t)); | |
790 | |
791 b->temporary = 1; | |
792 b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body; | |
793 b->start = cl->buf->start; | |
794 b->pos = cl->buf->pos; | |
795 b->last = cl->buf->last; | |
796 b->end = cl->buf->end; | |
797 | |
798 size = cl->buf->last - cl->buf->pos; | |
799 | |
800 if ((off_t) size < rb->rest) { | |
801 cl->buf->pos = cl->buf->last; | |
802 rb->rest -= size; | |
803 | |
804 } else { | |
805 cl->buf->pos += rb->rest; | |
806 rb->rest = 0; | |
807 b->last = cl->buf->pos; | |
808 b->last_buf = 1; | |
809 } | |
810 | |
811 *ll = tl; | |
812 ll = &tl->next; | |
813 } | |
814 | |
815 rc = ngx_http_request_body_save_filter(r, out); | |
816 | |
817 ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out, | |
818 (ngx_buf_tag_t) &ngx_http_read_client_request_body); | |
819 | |
820 return rc; | |
821 } | |
822 | |
823 | |
824 static ngx_int_t | |
825 ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) | |
826 { | |
827 size_t size; | |
828 ngx_int_t rc; | |
829 ngx_buf_t *b; | |
830 ngx_chain_t *cl, *out, *tl, **ll; | |
831 ngx_http_request_body_t *rb; | |
832 ngx_http_core_loc_conf_t *clcf; | |
833 | |
834 rb = r->request_body; | |
835 | |
836 if (rb->rest == -1) { | |
837 | |
838 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
839 "http request body chunked filter"); | |
840 | |
841 rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t)); | |
842 if (rb->chunked == NULL) { | |
843 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
844 } | |
845 | |
846 r->headers_in.content_length_n = 0; | |
847 rb->rest = 3; | |
848 } | |
849 | |
850 out = NULL; | |
851 ll = &out; | |
852 | |
853 for (cl = in; cl; cl = cl->next) { | |
854 | |
855 for ( ;; ) { | |
856 | |
857 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, | |
858 "http body chunked buf " | |
859 "t:%d f:%d %p, pos %p, size: %z file: %O, size: %z", | |
860 cl->buf->temporary, cl->buf->in_file, | |
861 cl->buf->start, cl->buf->pos, | |
862 cl->buf->last - cl->buf->pos, | |
863 cl->buf->file_pos, | |
864 cl->buf->file_last - cl->buf->file_pos); | |
865 | |
866 rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked); | |
867 | |
868 if (rc == NGX_OK) { | |
869 | |
870 /* a chunk has been parsed successfully */ | |
871 | |
872 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
873 | |
874 if (clcf->client_max_body_size | |
875 && clcf->client_max_body_size | |
876 < r->headers_in.content_length_n + rb->chunked->size) | |
877 { | |
878 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
879 "client intended to send too large chunked " | |
880 "body: %O bytes", | |
881 r->headers_in.content_length_n | |
882 + rb->chunked->size); | |
883 | |
884 r->lingering_close = 1; | |
885 | |
886 return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; | |
887 } | |
888 | |
889 tl = ngx_chain_get_free_buf(r->pool, &rb->free); | |
890 if (tl == NULL) { | |
891 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
892 } | |
893 | |
894 b = tl->buf; | |
895 | |
896 ngx_memzero(b, sizeof(ngx_buf_t)); | |
897 | |
898 b->temporary = 1; | |
899 b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body; | |
900 b->start = cl->buf->start; | |
901 b->pos = cl->buf->pos; | |
902 b->last = cl->buf->last; | |
903 b->end = cl->buf->end; | |
904 | |
905 *ll = tl; | |
906 ll = &tl->next; | |
907 | |
908 size = cl->buf->last - cl->buf->pos; | |
909 | |
910 if ((off_t) size > rb->chunked->size) { | |
911 cl->buf->pos += rb->chunked->size; | |
912 r->headers_in.content_length_n += rb->chunked->size; | |
913 rb->chunked->size = 0; | |
914 | |
915 } else { | |
916 rb->chunked->size -= size; | |
917 r->headers_in.content_length_n += size; | |
918 cl->buf->pos = cl->buf->last; | |
919 } | |
920 | |
921 b->last = cl->buf->pos; | |
922 | |
923 continue; | |
924 } | |
925 | |
926 if (rc == NGX_DONE) { | |
927 | |
928 /* a whole response has been parsed successfully */ | |
929 | |
930 rb->rest = 0; | |
931 | |
932 tl = ngx_chain_get_free_buf(r->pool, &rb->free); | |
933 if (tl == NULL) { | |
934 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
935 } | |
936 | |
937 b = tl->buf; | |
938 | |
939 ngx_memzero(b, sizeof(ngx_buf_t)); | |
940 | |
941 b->last_buf = 1; | |
942 | |
943 *ll = tl; | |
944 ll = &tl->next; | |
945 | |
946 break; | |
947 } | |
948 | |
949 if (rc == NGX_AGAIN) { | |
950 | |
951 /* set rb->rest, amount of data we want to see next time */ | |
952 | |
953 rb->rest = rb->chunked->length; | |
954 | |
955 break; | |
956 } | |
957 | |
958 /* invalid */ | |
959 | |
960 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
961 "client sent invalid chunked body"); | |
962 | |
963 return NGX_HTTP_BAD_REQUEST; | |
964 } | |
965 } | |
966 | |
967 rc = ngx_http_request_body_save_filter(r, out); | |
968 | |
969 ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out, | |
970 (ngx_buf_tag_t) &ngx_http_read_client_request_body); | |
971 | |
972 return rc; | |
973 } | |
974 | |
975 | |
976 static ngx_int_t | |
977 ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in) | |
978 { | |
979 ngx_chain_t *cl; | |
980 ngx_http_request_body_t *rb; | |
981 | |
982 rb = r->request_body; | |
983 | |
984 #if (NGX_DEBUG) | |
985 | |
986 for (cl = rb->bufs; cl; cl = cl->next) { | |
987 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, | |
988 "http body old buf t:%d f:%d %p, pos %p, size: %z " | |
989 "file: %O, size: %z", | |
990 cl->buf->temporary, cl->buf->in_file, | |
991 cl->buf->start, cl->buf->pos, | |
992 cl->buf->last - cl->buf->pos, | |
993 cl->buf->file_pos, | |
994 cl->buf->file_last - cl->buf->file_pos); | |
995 } | |
996 | |
997 for (cl = in; cl; cl = cl->next) { | |
998 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, | |
999 "http body new buf t:%d f:%d %p, pos %p, size: %z " | |
1000 "file: %O, size: %z", | |
1001 cl->buf->temporary, cl->buf->in_file, | |
1002 cl->buf->start, cl->buf->pos, | |
1003 cl->buf->last - cl->buf->pos, | |
1004 cl->buf->file_pos, | |
1005 cl->buf->file_last - cl->buf->file_pos); | |
1006 } | |
1007 | |
1008 #endif | |
1009 | |
1010 /* TODO: coalesce neighbouring buffers */ | |
1011 | |
1012 ngx_chain_add_copy(r->pool, &rb->bufs, in); | |
1013 | |
1014 return NGX_OK; | |
1015 } |