/src/nginx/src/http/ngx_http_request_body.c
Line | Count | Source |
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 | | |
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); |
15 | | static ngx_int_t ngx_http_copy_pipelined_header(ngx_http_request_t *r, |
16 | | ngx_buf_t *buf); |
17 | | static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r); |
18 | | static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r); |
19 | | static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r, |
20 | | ngx_buf_t *b); |
21 | | static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r); |
22 | | |
23 | | static ngx_int_t ngx_http_request_body_filter(ngx_http_request_t *r, |
24 | | ngx_chain_t *in); |
25 | | static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r, |
26 | | ngx_chain_t *in); |
27 | | static ngx_int_t ngx_http_request_body_chunked_filter(ngx_http_request_t *r, |
28 | | ngx_chain_t *in); |
29 | | |
30 | | |
31 | | ngx_int_t |
32 | | ngx_http_read_client_request_body(ngx_http_request_t *r, |
33 | | ngx_http_client_body_handler_pt post_handler) |
34 | 14 | { |
35 | 14 | size_t preread; |
36 | 14 | ssize_t size; |
37 | 14 | ngx_int_t rc; |
38 | 14 | ngx_buf_t *b; |
39 | 14 | ngx_chain_t out; |
40 | 14 | ngx_http_request_body_t *rb; |
41 | 14 | ngx_http_core_loc_conf_t *clcf; |
42 | | |
43 | 14 | r->main->count++; |
44 | | |
45 | 14 | if (r != r->main || r->request_body || r->discard_body) { |
46 | 0 | r->request_body_no_buffering = 0; |
47 | 0 | post_handler(r); |
48 | 0 | return NGX_OK; |
49 | 0 | } |
50 | | |
51 | 14 | if (ngx_http_test_expect(r) != NGX_OK) { |
52 | 0 | rc = NGX_HTTP_INTERNAL_SERVER_ERROR; |
53 | 0 | goto done; |
54 | 0 | } |
55 | | |
56 | 14 | rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); |
57 | 14 | if (rb == NULL) { |
58 | 0 | rc = NGX_HTTP_INTERNAL_SERVER_ERROR; |
59 | 0 | goto done; |
60 | 0 | } |
61 | | |
62 | | /* |
63 | | * set by ngx_pcalloc(): |
64 | | * |
65 | | * rb->temp_file = NULL; |
66 | | * rb->bufs = NULL; |
67 | | * rb->buf = NULL; |
68 | | * rb->free = NULL; |
69 | | * rb->busy = NULL; |
70 | | * rb->chunked = NULL; |
71 | | * rb->received = 0; |
72 | | * rb->filter_need_buffering = 0; |
73 | | * rb->last_sent = 0; |
74 | | * rb->last_saved = 0; |
75 | | */ |
76 | | |
77 | 14 | rb->rest = -1; |
78 | 14 | rb->post_handler = post_handler; |
79 | | |
80 | 14 | r->request_body = rb; |
81 | | |
82 | 14 | if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) { |
83 | 6 | r->request_body_no_buffering = 0; |
84 | 6 | post_handler(r); |
85 | 6 | return NGX_OK; |
86 | 6 | } |
87 | | |
88 | 8 | #if (NGX_HTTP_V2) |
89 | 8 | if (r->stream) { |
90 | 0 | rc = ngx_http_v2_read_request_body(r); |
91 | 0 | goto done; |
92 | 0 | } |
93 | 8 | #endif |
94 | | |
95 | | #if (NGX_HTTP_V3) |
96 | | if (r->http_version == NGX_HTTP_VERSION_30) { |
97 | | rc = ngx_http_v3_read_request_body(r); |
98 | | goto done; |
99 | | } |
100 | | #endif |
101 | | |
102 | 8 | preread = r->header_in->last - r->header_in->pos; |
103 | | |
104 | 8 | if (preread) { |
105 | | |
106 | | /* there is the pre-read part of the request body */ |
107 | | |
108 | 6 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
109 | 6 | "http client request body preread %uz", preread); |
110 | | |
111 | 6 | out.buf = r->header_in; |
112 | 6 | out.next = NULL; |
113 | | |
114 | 6 | rc = ngx_http_request_body_filter(r, &out); |
115 | | |
116 | 6 | if (rc != NGX_OK) { |
117 | 0 | goto done; |
118 | 0 | } |
119 | | |
120 | 6 | r->request_length += preread - (r->header_in->last - r->header_in->pos); |
121 | | |
122 | 6 | if (!r->headers_in.chunked |
123 | 0 | && rb->rest > 0 |
124 | 0 | && rb->rest <= (off_t) (r->header_in->end - r->header_in->last)) |
125 | 0 | { |
126 | | /* the whole request body may be placed in r->header_in */ |
127 | |
|
128 | 0 | b = ngx_calloc_buf(r->pool); |
129 | 0 | if (b == NULL) { |
130 | 0 | rc = NGX_HTTP_INTERNAL_SERVER_ERROR; |
131 | 0 | goto done; |
132 | 0 | } |
133 | | |
134 | 0 | b->temporary = 1; |
135 | 0 | b->start = r->header_in->pos; |
136 | 0 | b->pos = r->header_in->pos; |
137 | 0 | b->last = r->header_in->last; |
138 | 0 | b->end = r->header_in->end; |
139 | |
|
140 | 0 | rb->buf = b; |
141 | |
|
142 | 0 | r->read_event_handler = ngx_http_read_client_request_body_handler; |
143 | 0 | r->write_event_handler = ngx_http_request_empty_handler; |
144 | |
|
145 | 0 | rc = ngx_http_do_read_client_request_body(r); |
146 | 0 | goto done; |
147 | 0 | } |
148 | | |
149 | 6 | } else { |
150 | | /* set rb->rest */ |
151 | | |
152 | 2 | rc = ngx_http_request_body_filter(r, NULL); |
153 | | |
154 | 2 | if (rc != NGX_OK) { |
155 | 0 | goto done; |
156 | 0 | } |
157 | 2 | } |
158 | | |
159 | 8 | if (rb->rest == 0 && rb->last_saved) { |
160 | | /* the whole request body was pre-read */ |
161 | 0 | r->request_body_no_buffering = 0; |
162 | 0 | post_handler(r); |
163 | 0 | return NGX_OK; |
164 | 0 | } |
165 | | |
166 | 8 | if (rb->rest < 0) { |
167 | 0 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, |
168 | 0 | "negative request body rest"); |
169 | 0 | rc = NGX_HTTP_INTERNAL_SERVER_ERROR; |
170 | 0 | goto done; |
171 | 0 | } |
172 | | |
173 | 8 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
174 | | |
175 | 8 | size = clcf->client_body_buffer_size; |
176 | 8 | size += size >> 2; |
177 | | |
178 | | /* TODO: honor r->request_body_in_single_buf */ |
179 | | |
180 | 8 | if (!r->headers_in.chunked && rb->rest < size) { |
181 | 0 | size = (ssize_t) rb->rest; |
182 | |
|
183 | 0 | if (r->request_body_in_single_buf) { |
184 | 0 | size += preread; |
185 | 0 | } |
186 | |
|
187 | 0 | if (size == 0) { |
188 | 0 | size++; |
189 | 0 | } |
190 | |
|
191 | 8 | } else { |
192 | 8 | size = clcf->client_body_buffer_size; |
193 | 8 | } |
194 | | |
195 | 8 | rb->buf = ngx_create_temp_buf(r->pool, size); |
196 | 8 | if (rb->buf == NULL) { |
197 | 0 | rc = NGX_HTTP_INTERNAL_SERVER_ERROR; |
198 | 0 | goto done; |
199 | 0 | } |
200 | | |
201 | 8 | r->read_event_handler = ngx_http_read_client_request_body_handler; |
202 | 8 | r->write_event_handler = ngx_http_request_empty_handler; |
203 | | |
204 | 8 | rc = ngx_http_do_read_client_request_body(r); |
205 | | |
206 | 8 | done: |
207 | | |
208 | 8 | if (r->request_body_no_buffering |
209 | 0 | && (rc == NGX_OK || rc == NGX_AGAIN)) |
210 | 0 | { |
211 | 0 | if (rc == NGX_OK) { |
212 | 0 | r->request_body_no_buffering = 0; |
213 | |
|
214 | 0 | } else { |
215 | | /* rc == NGX_AGAIN */ |
216 | 0 | r->reading_body = 1; |
217 | 0 | } |
218 | |
|
219 | 0 | r->read_event_handler = ngx_http_block_reading; |
220 | 0 | post_handler(r); |
221 | 0 | } |
222 | | |
223 | 8 | if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { |
224 | 8 | r->main->count--; |
225 | 8 | } |
226 | | |
227 | 8 | return rc; |
228 | 8 | } |
229 | | |
230 | | |
231 | | ngx_int_t |
232 | | ngx_http_read_unbuffered_request_body(ngx_http_request_t *r) |
233 | 0 | { |
234 | 0 | ngx_int_t rc; |
235 | |
|
236 | 0 | #if (NGX_HTTP_V2) |
237 | 0 | if (r->stream) { |
238 | 0 | rc = ngx_http_v2_read_unbuffered_request_body(r); |
239 | |
|
240 | 0 | if (rc == NGX_OK) { |
241 | 0 | r->reading_body = 0; |
242 | 0 | } |
243 | |
|
244 | 0 | return rc; |
245 | 0 | } |
246 | 0 | #endif |
247 | | |
248 | | #if (NGX_HTTP_V3) |
249 | | if (r->http_version == NGX_HTTP_VERSION_30) { |
250 | | rc = ngx_http_v3_read_unbuffered_request_body(r); |
251 | | |
252 | | if (rc == NGX_OK) { |
253 | | r->reading_body = 0; |
254 | | } |
255 | | |
256 | | return rc; |
257 | | } |
258 | | #endif |
259 | | |
260 | 0 | if (r->connection->read->timedout) { |
261 | 0 | r->connection->timedout = 1; |
262 | 0 | return NGX_HTTP_REQUEST_TIME_OUT; |
263 | 0 | } |
264 | | |
265 | 0 | rc = ngx_http_do_read_client_request_body(r); |
266 | |
|
267 | 0 | if (rc == NGX_OK) { |
268 | 0 | r->reading_body = 0; |
269 | 0 | } |
270 | |
|
271 | 0 | return rc; |
272 | 0 | } |
273 | | |
274 | | |
275 | | static void |
276 | | ngx_http_read_client_request_body_handler(ngx_http_request_t *r) |
277 | 0 | { |
278 | 0 | ngx_int_t rc; |
279 | |
|
280 | 0 | if (r->connection->read->timedout) { |
281 | 0 | r->connection->timedout = 1; |
282 | 0 | ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); |
283 | 0 | return; |
284 | 0 | } |
285 | | |
286 | 0 | rc = ngx_http_do_read_client_request_body(r); |
287 | |
|
288 | 0 | if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { |
289 | 0 | ngx_http_finalize_request(r, rc); |
290 | 0 | } |
291 | 0 | } |
292 | | |
293 | | |
294 | | static ngx_int_t |
295 | | ngx_http_do_read_client_request_body(ngx_http_request_t *r) |
296 | 8 | { |
297 | 8 | off_t rest; |
298 | 8 | size_t size; |
299 | 8 | ssize_t n; |
300 | 8 | ngx_int_t rc; |
301 | 8 | ngx_uint_t flush; |
302 | 8 | ngx_chain_t out; |
303 | 8 | ngx_connection_t *c; |
304 | 8 | ngx_http_request_body_t *rb; |
305 | 8 | ngx_http_core_loc_conf_t *clcf; |
306 | | |
307 | 8 | c = r->connection; |
308 | 8 | rb = r->request_body; |
309 | 8 | flush = 1; |
310 | | |
311 | 8 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, |
312 | 8 | "http read client request body"); |
313 | | |
314 | 13 | for ( ;; ) { |
315 | 280 | for ( ;; ) { |
316 | 280 | if (rb->rest == 0) { |
317 | 0 | break; |
318 | 0 | } |
319 | | |
320 | 280 | if (rb->buf->last == rb->buf->end) { |
321 | | |
322 | | /* update chains */ |
323 | | |
324 | 267 | rc = ngx_http_request_body_filter(r, NULL); |
325 | | |
326 | 267 | if (rc != NGX_OK) { |
327 | 0 | return rc; |
328 | 0 | } |
329 | | |
330 | 267 | if (rb->busy != NULL) { |
331 | 0 | if (r->request_body_no_buffering) { |
332 | 0 | if (c->read->timer_set) { |
333 | 0 | ngx_del_timer(c->read); |
334 | 0 | } |
335 | |
|
336 | 0 | if (ngx_handle_read_event(c->read, 0) != NGX_OK) { |
337 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
338 | 0 | } |
339 | | |
340 | 0 | return NGX_AGAIN; |
341 | 0 | } |
342 | | |
343 | 0 | if (rb->filter_need_buffering) { |
344 | 0 | clcf = ngx_http_get_module_loc_conf(r, |
345 | 0 | ngx_http_core_module); |
346 | 0 | ngx_add_timer(c->read, clcf->client_body_timeout); |
347 | |
|
348 | 0 | if (ngx_handle_read_event(c->read, 0) != NGX_OK) { |
349 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
350 | 0 | } |
351 | | |
352 | 0 | return NGX_AGAIN; |
353 | 0 | } |
354 | | |
355 | 0 | ngx_log_error(NGX_LOG_ALERT, c->log, 0, |
356 | 0 | "busy buffers after request body flush"); |
357 | |
|
358 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
359 | 0 | } |
360 | | |
361 | 267 | flush = 0; |
362 | 267 | rb->buf->pos = rb->buf->start; |
363 | 267 | rb->buf->last = rb->buf->start; |
364 | 267 | } |
365 | | |
366 | 280 | size = rb->buf->end - rb->buf->last; |
367 | 280 | rest = rb->rest - (rb->buf->last - rb->buf->pos); |
368 | | |
369 | 280 | if ((off_t) size > rest) { |
370 | 0 | size = (size_t) rest; |
371 | 0 | } |
372 | | |
373 | 280 | if (size == 0) { |
374 | 0 | break; |
375 | 0 | } |
376 | | |
377 | 280 | n = c->recv(c, rb->buf->last, size); |
378 | | |
379 | 280 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, |
380 | 280 | "http client request body recv %z", n); |
381 | | |
382 | 280 | if (n == NGX_AGAIN) { |
383 | 0 | break; |
384 | 0 | } |
385 | | |
386 | 280 | if (n == 0) { |
387 | 5 | ngx_log_error(NGX_LOG_INFO, c->log, 0, |
388 | 5 | "client prematurely closed connection"); |
389 | 5 | } |
390 | | |
391 | 280 | if (n == 0 || n == NGX_ERROR) { |
392 | 5 | c->error = 1; |
393 | 5 | return NGX_HTTP_BAD_REQUEST; |
394 | 5 | } |
395 | | |
396 | 275 | rb->buf->last += n; |
397 | 275 | r->request_length += n; |
398 | | |
399 | | /* pass buffer to request body filter chain */ |
400 | | |
401 | 275 | flush = 0; |
402 | 275 | out.buf = rb->buf; |
403 | 275 | out.next = NULL; |
404 | | |
405 | 275 | rc = ngx_http_request_body_filter(r, &out); |
406 | | |
407 | 275 | if (rc != NGX_OK) { |
408 | 3 | return rc; |
409 | 3 | } |
410 | | |
411 | 272 | if (rb->rest == 0) { |
412 | 0 | break; |
413 | 0 | } |
414 | | |
415 | 272 | if (rb->buf->last < rb->buf->end) { |
416 | 5 | break; |
417 | 5 | } |
418 | 272 | } |
419 | | |
420 | 5 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, |
421 | 5 | "http client request body rest %O", rb->rest); |
422 | | |
423 | 5 | if (flush) { |
424 | 0 | rc = ngx_http_request_body_filter(r, NULL); |
425 | |
|
426 | 0 | if (rc != NGX_OK) { |
427 | 0 | return rc; |
428 | 0 | } |
429 | 0 | } |
430 | | |
431 | 5 | if (rb->rest == 0 && rb->last_saved) { |
432 | 0 | break; |
433 | 0 | } |
434 | | |
435 | 5 | if (!c->read->ready || rb->rest == 0) { |
436 | |
|
437 | 0 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
438 | 0 | ngx_add_timer(c->read, clcf->client_body_timeout); |
439 | |
|
440 | 0 | if (ngx_handle_read_event(c->read, 0) != NGX_OK) { |
441 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
442 | 0 | } |
443 | | |
444 | 0 | return NGX_AGAIN; |
445 | 0 | } |
446 | 5 | } |
447 | | |
448 | 0 | if (ngx_http_copy_pipelined_header(r, rb->buf) != NGX_OK) { |
449 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
450 | 0 | } |
451 | | |
452 | 0 | if (c->read->timer_set) { |
453 | 0 | ngx_del_timer(c->read); |
454 | 0 | } |
455 | |
|
456 | 0 | if (!r->request_body_no_buffering) { |
457 | 0 | r->read_event_handler = ngx_http_block_reading; |
458 | 0 | rb->post_handler(r); |
459 | 0 | } |
460 | |
|
461 | 0 | return NGX_OK; |
462 | 0 | } |
463 | | |
464 | | |
465 | | static ngx_int_t |
466 | | ngx_http_copy_pipelined_header(ngx_http_request_t *r, ngx_buf_t *buf) |
467 | 0 | { |
468 | 0 | size_t n; |
469 | 0 | ngx_buf_t *b; |
470 | 0 | ngx_chain_t *cl; |
471 | 0 | ngx_http_connection_t *hc; |
472 | 0 | ngx_http_core_srv_conf_t *cscf; |
473 | |
|
474 | 0 | b = r->header_in; |
475 | 0 | n = buf->last - buf->pos; |
476 | |
|
477 | 0 | if (buf == b || n == 0) { |
478 | 0 | return NGX_OK; |
479 | 0 | } |
480 | | |
481 | 0 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
482 | 0 | "http body pipelined header: %uz", n); |
483 | | |
484 | | /* |
485 | | * if there is a pipelined request in the client body buffer, |
486 | | * copy it to the r->header_in buffer if there is enough room, |
487 | | * or allocate a large client header buffer |
488 | | */ |
489 | |
|
490 | 0 | if (n > (size_t) (b->end - b->last)) { |
491 | |
|
492 | 0 | hc = r->http_connection; |
493 | |
|
494 | 0 | if (hc->free) { |
495 | 0 | cl = hc->free; |
496 | 0 | hc->free = cl->next; |
497 | |
|
498 | 0 | b = cl->buf; |
499 | |
|
500 | 0 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
501 | 0 | "http large header free: %p %uz", |
502 | 0 | b->pos, b->end - b->last); |
503 | |
|
504 | 0 | } else { |
505 | 0 | cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); |
506 | |
|
507 | 0 | b = ngx_create_temp_buf(r->connection->pool, |
508 | 0 | cscf->large_client_header_buffers.size); |
509 | 0 | if (b == NULL) { |
510 | 0 | return NGX_ERROR; |
511 | 0 | } |
512 | | |
513 | 0 | cl = ngx_alloc_chain_link(r->connection->pool); |
514 | 0 | if (cl == NULL) { |
515 | 0 | return NGX_ERROR; |
516 | 0 | } |
517 | | |
518 | 0 | cl->buf = b; |
519 | |
|
520 | 0 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
521 | 0 | "http large header alloc: %p %uz", |
522 | 0 | b->pos, b->end - b->last); |
523 | 0 | } |
524 | | |
525 | 0 | cl->next = hc->busy; |
526 | 0 | hc->busy = cl; |
527 | 0 | hc->nbusy++; |
528 | |
|
529 | 0 | r->header_in = b; |
530 | |
|
531 | 0 | if (n > (size_t) (b->end - b->last)) { |
532 | 0 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, |
533 | 0 | "too large pipelined header after reading body"); |
534 | 0 | return NGX_ERROR; |
535 | 0 | } |
536 | 0 | } |
537 | | |
538 | 0 | ngx_memcpy(b->last, buf->pos, n); |
539 | |
|
540 | 0 | b->last += n; |
541 | 0 | r->request_length -= n; |
542 | |
|
543 | 0 | return NGX_OK; |
544 | 0 | } |
545 | | |
546 | | |
547 | | static ngx_int_t |
548 | | ngx_http_write_request_body(ngx_http_request_t *r) |
549 | 214 | { |
550 | 214 | ssize_t n; |
551 | 214 | ngx_chain_t *cl, *ln; |
552 | 214 | ngx_temp_file_t *tf; |
553 | 214 | ngx_http_request_body_t *rb; |
554 | 214 | ngx_http_core_loc_conf_t *clcf; |
555 | | |
556 | 214 | rb = r->request_body; |
557 | | |
558 | 214 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
559 | 214 | "http write client request body, bufs %p", rb->bufs); |
560 | | |
561 | 214 | if (rb->temp_file == NULL) { |
562 | 7 | tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); |
563 | 7 | if (tf == NULL) { |
564 | 0 | return NGX_ERROR; |
565 | 0 | } |
566 | | |
567 | 7 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
568 | | |
569 | 7 | tf->file.fd = NGX_INVALID_FILE; |
570 | 7 | tf->file.log = r->connection->log; |
571 | 7 | tf->path = clcf->client_body_temp_path; |
572 | 7 | tf->pool = r->pool; |
573 | 7 | tf->warn = "a client request body is buffered to a temporary file"; |
574 | 7 | tf->log_level = r->request_body_file_log_level; |
575 | 7 | tf->persistent = r->request_body_in_persistent_file; |
576 | 7 | tf->clean = r->request_body_in_clean_file; |
577 | | |
578 | 7 | if (r->request_body_file_group_access) { |
579 | 0 | tf->access = 0660; |
580 | 0 | } |
581 | | |
582 | 7 | rb->temp_file = tf; |
583 | | |
584 | 7 | if (rb->bufs == NULL |
585 | 7 | || (!ngx_buf_in_memory(rb->bufs->buf) && rb->bufs->buf->last_buf)) |
586 | 0 | { |
587 | | /* empty body with r->request_body_in_file_only */ |
588 | |
|
589 | 0 | if (ngx_create_temp_file(&tf->file, tf->path, tf->pool, |
590 | 0 | tf->persistent, tf->clean, tf->access) |
591 | 0 | != NGX_OK) |
592 | 0 | { |
593 | 0 | return NGX_ERROR; |
594 | 0 | } |
595 | | |
596 | 0 | return NGX_OK; |
597 | 0 | } |
598 | 7 | } |
599 | | |
600 | 214 | if (rb->bufs == NULL) { |
601 | 0 | return NGX_OK; |
602 | 0 | } |
603 | | |
604 | 214 | n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs); |
605 | | |
606 | | /* TODO: n == 0 or not complete and level event */ |
607 | | |
608 | 214 | if (n == NGX_ERROR) { |
609 | 0 | return NGX_ERROR; |
610 | 0 | } |
611 | | |
612 | 214 | rb->temp_file->offset += n; |
613 | | |
614 | | /* mark all buffers as written */ |
615 | | |
616 | 571 | for (cl = rb->bufs; cl; /* void */) { |
617 | | |
618 | 357 | cl->buf->pos = cl->buf->last; |
619 | | |
620 | 357 | ln = cl; |
621 | 357 | cl = cl->next; |
622 | 357 | ngx_free_chain(r->pool, ln); |
623 | 357 | } |
624 | | |
625 | 214 | rb->bufs = NULL; |
626 | | |
627 | 214 | return NGX_OK; |
628 | 214 | } |
629 | | |
630 | | |
631 | | ngx_int_t |
632 | | ngx_http_discard_request_body(ngx_http_request_t *r) |
633 | 11 | { |
634 | 11 | ssize_t size; |
635 | 11 | ngx_int_t rc; |
636 | 11 | ngx_event_t *rev; |
637 | | |
638 | 11 | if (r != r->main || r->discard_body || r->request_body) { |
639 | 3 | return NGX_OK; |
640 | 3 | } |
641 | | |
642 | 8 | #if (NGX_HTTP_V2) |
643 | 8 | if (r->stream) { |
644 | 0 | r->stream->skip_data = 1; |
645 | 0 | return NGX_OK; |
646 | 0 | } |
647 | 8 | #endif |
648 | | |
649 | | #if (NGX_HTTP_V3) |
650 | | if (r->http_version == NGX_HTTP_VERSION_30) { |
651 | | return NGX_OK; |
652 | | } |
653 | | #endif |
654 | | |
655 | 8 | if (ngx_http_test_expect(r) != NGX_OK) { |
656 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
657 | 0 | } |
658 | | |
659 | 8 | rev = r->connection->read; |
660 | | |
661 | 8 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); |
662 | | |
663 | 8 | if (rev->timer_set) { |
664 | 0 | ngx_del_timer(rev); |
665 | 0 | } |
666 | | |
667 | 8 | if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) { |
668 | 5 | return NGX_OK; |
669 | 5 | } |
670 | | |
671 | 3 | size = r->header_in->last - r->header_in->pos; |
672 | | |
673 | 3 | if (size || r->headers_in.chunked) { |
674 | 2 | rc = ngx_http_discard_request_body_filter(r, r->header_in); |
675 | | |
676 | 2 | if (rc != NGX_OK) { |
677 | 0 | return rc; |
678 | 0 | } |
679 | | |
680 | 2 | if (r->headers_in.content_length_n == 0) { |
681 | 0 | return NGX_OK; |
682 | 0 | } |
683 | 2 | } |
684 | | |
685 | 3 | rc = ngx_http_read_discarded_request_body(r); |
686 | | |
687 | 3 | if (rc == NGX_OK) { |
688 | 2 | r->lingering_close = 0; |
689 | 2 | return NGX_OK; |
690 | 2 | } |
691 | | |
692 | 1 | if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { |
693 | 1 | return rc; |
694 | 1 | } |
695 | | |
696 | | /* rc == NGX_AGAIN */ |
697 | | |
698 | 0 | r->read_event_handler = ngx_http_discarded_request_body_handler; |
699 | |
|
700 | 0 | if (ngx_handle_read_event(rev, 0) != NGX_OK) { |
701 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
702 | 0 | } |
703 | | |
704 | 0 | r->count++; |
705 | 0 | r->discard_body = 1; |
706 | |
|
707 | 0 | return NGX_OK; |
708 | 0 | } |
709 | | |
710 | | |
711 | | void |
712 | | ngx_http_discarded_request_body_handler(ngx_http_request_t *r) |
713 | 0 | { |
714 | 0 | ngx_int_t rc; |
715 | 0 | ngx_msec_t timer; |
716 | 0 | ngx_event_t *rev; |
717 | 0 | ngx_connection_t *c; |
718 | 0 | ngx_http_core_loc_conf_t *clcf; |
719 | |
|
720 | 0 | c = r->connection; |
721 | 0 | rev = c->read; |
722 | |
|
723 | 0 | if (rev->timedout) { |
724 | 0 | c->timedout = 1; |
725 | 0 | c->error = 1; |
726 | 0 | ngx_http_finalize_request(r, NGX_ERROR); |
727 | 0 | return; |
728 | 0 | } |
729 | | |
730 | 0 | if (r->lingering_time) { |
731 | 0 | timer = (ngx_msec_t) r->lingering_time - (ngx_msec_t) ngx_time(); |
732 | |
|
733 | 0 | if ((ngx_msec_int_t) timer <= 0) { |
734 | 0 | r->discard_body = 0; |
735 | 0 | r->lingering_close = 0; |
736 | 0 | ngx_http_finalize_request(r, NGX_ERROR); |
737 | 0 | return; |
738 | 0 | } |
739 | |
|
740 | 0 | } else { |
741 | 0 | timer = 0; |
742 | 0 | } |
743 | | |
744 | 0 | rc = ngx_http_read_discarded_request_body(r); |
745 | |
|
746 | 0 | if (rc == NGX_OK) { |
747 | 0 | r->discard_body = 0; |
748 | 0 | r->lingering_close = 0; |
749 | 0 | r->lingering_time = 0; |
750 | 0 | ngx_http_finalize_request(r, NGX_DONE); |
751 | 0 | return; |
752 | 0 | } |
753 | | |
754 | 0 | if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { |
755 | 0 | c->error = 1; |
756 | 0 | ngx_http_finalize_request(r, NGX_ERROR); |
757 | 0 | return; |
758 | 0 | } |
759 | | |
760 | | /* rc == NGX_AGAIN */ |
761 | | |
762 | 0 | if (ngx_handle_read_event(rev, 0) != NGX_OK) { |
763 | 0 | c->error = 1; |
764 | 0 | ngx_http_finalize_request(r, NGX_ERROR); |
765 | 0 | return; |
766 | 0 | } |
767 | | |
768 | 0 | if (timer) { |
769 | |
|
770 | 0 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
771 | |
|
772 | 0 | timer *= 1000; |
773 | |
|
774 | 0 | if (timer > clcf->lingering_timeout) { |
775 | 0 | timer = clcf->lingering_timeout; |
776 | 0 | } |
777 | |
|
778 | 0 | ngx_add_timer(rev, timer); |
779 | 0 | } |
780 | 0 | } |
781 | | |
782 | | |
783 | | static ngx_int_t |
784 | | ngx_http_read_discarded_request_body(ngx_http_request_t *r) |
785 | 3 | { |
786 | 3 | size_t size; |
787 | 3 | ssize_t n; |
788 | 3 | ngx_int_t rc; |
789 | 3 | ngx_buf_t b; |
790 | 3 | u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE]; |
791 | | |
792 | 3 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
793 | 3 | "http read discarded body"); |
794 | | |
795 | 3 | ngx_memzero(&b, sizeof(ngx_buf_t)); |
796 | | |
797 | 3 | b.temporary = 1; |
798 | | |
799 | 162 | for ( ;; ) { |
800 | 162 | if (r->headers_in.content_length_n == 0) { |
801 | 0 | break; |
802 | 0 | } |
803 | | |
804 | 162 | if (!r->connection->read->ready) { |
805 | 0 | return NGX_AGAIN; |
806 | 0 | } |
807 | | |
808 | 162 | size = (size_t) ngx_min(r->headers_in.content_length_n, |
809 | 162 | NGX_HTTP_DISCARD_BUFFER_SIZE); |
810 | | |
811 | 162 | n = r->connection->recv(r->connection, buffer, size); |
812 | | |
813 | 162 | if (n == NGX_ERROR) { |
814 | 0 | r->connection->error = 1; |
815 | 0 | return NGX_OK; |
816 | 0 | } |
817 | | |
818 | 162 | if (n == NGX_AGAIN) { |
819 | 0 | return NGX_AGAIN; |
820 | 0 | } |
821 | | |
822 | 162 | if (n == 0) { |
823 | 2 | return NGX_OK; |
824 | 2 | } |
825 | | |
826 | 160 | b.pos = buffer; |
827 | 160 | b.last = buffer + n; |
828 | | |
829 | 160 | rc = ngx_http_discard_request_body_filter(r, &b); |
830 | | |
831 | 160 | if (rc != NGX_OK) { |
832 | 1 | return rc; |
833 | 1 | } |
834 | 160 | } |
835 | | |
836 | 0 | if (ngx_http_copy_pipelined_header(r, &b) != NGX_OK) { |
837 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
838 | 0 | } |
839 | | |
840 | 0 | r->read_event_handler = ngx_http_block_reading; |
841 | |
|
842 | 0 | return NGX_OK; |
843 | 0 | } |
844 | | |
845 | | |
846 | | static ngx_int_t |
847 | | ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b) |
848 | 162 | { |
849 | 162 | size_t size; |
850 | 162 | ngx_int_t rc; |
851 | 162 | ngx_http_request_body_t *rb; |
852 | 162 | ngx_http_core_srv_conf_t *cscf; |
853 | | |
854 | 162 | if (r->headers_in.chunked) { |
855 | | |
856 | 130 | rb = r->request_body; |
857 | | |
858 | 130 | if (rb == NULL) { |
859 | | |
860 | 1 | rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); |
861 | 1 | if (rb == NULL) { |
862 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
863 | 0 | } |
864 | | |
865 | 1 | rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t)); |
866 | 1 | if (rb->chunked == NULL) { |
867 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
868 | 0 | } |
869 | | |
870 | 1 | r->request_body = rb; |
871 | 1 | } |
872 | | |
873 | 130 | for ( ;; ) { |
874 | | |
875 | 130 | rc = ngx_http_parse_chunked(r, b, rb->chunked, 0); |
876 | | |
877 | 130 | if (rc == NGX_OK) { |
878 | | |
879 | | /* a chunk has been parsed successfully */ |
880 | |
|
881 | 0 | size = b->last - b->pos; |
882 | |
|
883 | 0 | if ((off_t) size > rb->chunked->size) { |
884 | 0 | b->pos += (size_t) rb->chunked->size; |
885 | 0 | rb->chunked->size = 0; |
886 | |
|
887 | 0 | } else { |
888 | 0 | rb->chunked->size -= size; |
889 | 0 | b->pos = b->last; |
890 | 0 | } |
891 | |
|
892 | 0 | continue; |
893 | 0 | } |
894 | | |
895 | 130 | if (rc == NGX_DONE) { |
896 | | |
897 | | /* a whole response has been parsed successfully */ |
898 | |
|
899 | 0 | r->headers_in.content_length_n = 0; |
900 | 0 | break; |
901 | 0 | } |
902 | | |
903 | 130 | if (rc == NGX_AGAIN) { |
904 | | |
905 | | /* set amount of data we want to see next time */ |
906 | | |
907 | 129 | cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); |
908 | | |
909 | 129 | r->headers_in.content_length_n = ngx_max(rb->chunked->length, |
910 | 129 | (off_t) cscf->large_client_header_buffers.size); |
911 | 129 | break; |
912 | 129 | } |
913 | | |
914 | | /* invalid */ |
915 | | |
916 | 1 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
917 | 1 | "client sent invalid chunked body"); |
918 | | |
919 | 1 | return NGX_HTTP_BAD_REQUEST; |
920 | 130 | } |
921 | | |
922 | 130 | } else { |
923 | 32 | size = b->last - b->pos; |
924 | | |
925 | 32 | if ((off_t) size > r->headers_in.content_length_n) { |
926 | 0 | b->pos += (size_t) r->headers_in.content_length_n; |
927 | 0 | r->headers_in.content_length_n = 0; |
928 | |
|
929 | 32 | } else { |
930 | 32 | b->pos = b->last; |
931 | 32 | r->headers_in.content_length_n -= size; |
932 | 32 | } |
933 | 32 | } |
934 | | |
935 | 161 | return NGX_OK; |
936 | 162 | } |
937 | | |
938 | | |
939 | | static ngx_int_t |
940 | | ngx_http_test_expect(ngx_http_request_t *r) |
941 | 22 | { |
942 | 22 | ngx_int_t n; |
943 | 22 | ngx_str_t *expect; |
944 | | |
945 | 22 | if (r->expect_tested |
946 | 14 | || r->headers_in.expect == NULL |
947 | 0 | || r->http_version < NGX_HTTP_VERSION_11 |
948 | 0 | #if (NGX_HTTP_V2) |
949 | 0 | || r->stream != NULL |
950 | 22 | #endif |
951 | | #if (NGX_HTTP_V3) |
952 | | || r->connection->quic != NULL |
953 | | #endif |
954 | 22 | ) |
955 | 22 | { |
956 | 22 | return NGX_OK; |
957 | 22 | } |
958 | | |
959 | 0 | r->expect_tested = 1; |
960 | |
|
961 | 0 | expect = &r->headers_in.expect->value; |
962 | |
|
963 | 0 | if (expect->len != sizeof("100-continue") - 1 |
964 | 0 | || ngx_strncasecmp(expect->data, (u_char *) "100-continue", |
965 | 0 | sizeof("100-continue") - 1) |
966 | 0 | != 0) |
967 | 0 | { |
968 | 0 | return NGX_OK; |
969 | 0 | } |
970 | | |
971 | 0 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
972 | 0 | "send 100 Continue"); |
973 | |
|
974 | 0 | n = r->connection->send(r->connection, |
975 | 0 | (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF, |
976 | 0 | sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1); |
977 | |
|
978 | 0 | if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) { |
979 | 0 | return NGX_OK; |
980 | 0 | } |
981 | | |
982 | | /* we assume that such small packet should be send successfully */ |
983 | | |
984 | 0 | r->connection->error = 1; |
985 | |
|
986 | 0 | return NGX_ERROR; |
987 | 0 | } |
988 | | |
989 | | |
990 | | static ngx_int_t |
991 | | ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) |
992 | 550 | { |
993 | 550 | if (r->headers_in.chunked) { |
994 | 352 | return ngx_http_request_body_chunked_filter(r, in); |
995 | | |
996 | 352 | } else { |
997 | 198 | return ngx_http_request_body_length_filter(r, in); |
998 | 198 | } |
999 | 550 | } |
1000 | | |
1001 | | |
1002 | | static ngx_int_t |
1003 | | ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in) |
1004 | 198 | { |
1005 | 198 | size_t size; |
1006 | 198 | ngx_int_t rc; |
1007 | 198 | ngx_buf_t *b; |
1008 | 198 | ngx_chain_t *cl, *tl, *out, **ll; |
1009 | 198 | ngx_http_request_body_t *rb; |
1010 | | |
1011 | 198 | rb = r->request_body; |
1012 | | |
1013 | 198 | out = NULL; |
1014 | 198 | ll = &out; |
1015 | | |
1016 | 198 | if (rb->rest == -1) { |
1017 | 2 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
1018 | 2 | "http request body content length filter"); |
1019 | | |
1020 | 2 | rb->rest = r->headers_in.content_length_n; |
1021 | | |
1022 | 2 | if (rb->rest == 0) { |
1023 | |
|
1024 | 0 | tl = ngx_chain_get_free_buf(r->pool, &rb->free); |
1025 | 0 | if (tl == NULL) { |
1026 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
1027 | 0 | } |
1028 | | |
1029 | 0 | b = tl->buf; |
1030 | |
|
1031 | 0 | ngx_memzero(b, sizeof(ngx_buf_t)); |
1032 | |
|
1033 | 0 | b->last_buf = 1; |
1034 | |
|
1035 | 0 | *ll = tl; |
1036 | 0 | ll = &tl->next; |
1037 | 0 | } |
1038 | 2 | } |
1039 | | |
1040 | 297 | for (cl = in; cl; cl = cl->next) { |
1041 | | |
1042 | 99 | if (rb->rest == 0) { |
1043 | 0 | break; |
1044 | 0 | } |
1045 | | |
1046 | 99 | tl = ngx_chain_get_free_buf(r->pool, &rb->free); |
1047 | 99 | if (tl == NULL) { |
1048 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
1049 | 0 | } |
1050 | | |
1051 | 99 | b = tl->buf; |
1052 | | |
1053 | 99 | ngx_memzero(b, sizeof(ngx_buf_t)); |
1054 | | |
1055 | 99 | b->temporary = 1; |
1056 | 99 | b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body; |
1057 | 99 | b->start = cl->buf->pos; |
1058 | 99 | b->pos = cl->buf->pos; |
1059 | 99 | b->last = cl->buf->last; |
1060 | 99 | b->end = cl->buf->end; |
1061 | 99 | b->flush = r->request_body_no_buffering; |
1062 | | |
1063 | 99 | size = cl->buf->last - cl->buf->pos; |
1064 | | |
1065 | 99 | if ((off_t) size < rb->rest) { |
1066 | 99 | cl->buf->pos = cl->buf->last; |
1067 | 99 | rb->rest -= size; |
1068 | | |
1069 | 99 | } else { |
1070 | 0 | cl->buf->pos += (size_t) rb->rest; |
1071 | 0 | rb->rest = 0; |
1072 | 0 | b->last = cl->buf->pos; |
1073 | 0 | b->last_buf = 1; |
1074 | 0 | } |
1075 | | |
1076 | 99 | *ll = tl; |
1077 | 99 | ll = &tl->next; |
1078 | 99 | } |
1079 | | |
1080 | 198 | rc = ngx_http_top_request_body_filter(r, out); |
1081 | | |
1082 | 198 | ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out, |
1083 | 198 | (ngx_buf_tag_t) &ngx_http_read_client_request_body); |
1084 | | |
1085 | 198 | return rc; |
1086 | 198 | } |
1087 | | |
1088 | | |
1089 | | static ngx_int_t |
1090 | | ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in) |
1091 | 352 | { |
1092 | 352 | size_t size; |
1093 | 352 | ngx_int_t rc; |
1094 | 352 | ngx_buf_t *b; |
1095 | 352 | ngx_chain_t *cl, *out, *tl, **ll; |
1096 | 352 | ngx_http_request_body_t *rb; |
1097 | 352 | ngx_http_core_loc_conf_t *clcf; |
1098 | 352 | ngx_http_core_srv_conf_t *cscf; |
1099 | | |
1100 | 352 | rb = r->request_body; |
1101 | | |
1102 | 352 | out = NULL; |
1103 | 352 | ll = &out; |
1104 | | |
1105 | 352 | if (rb->rest == -1) { |
1106 | | |
1107 | 6 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
1108 | 6 | "http request body chunked filter"); |
1109 | | |
1110 | 6 | rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t)); |
1111 | 6 | if (rb->chunked == NULL) { |
1112 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
1113 | 0 | } |
1114 | | |
1115 | 6 | cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); |
1116 | | |
1117 | 6 | r->headers_in.content_length_n = 0; |
1118 | 6 | rb->rest = cscf->large_client_header_buffers.size; |
1119 | 6 | } |
1120 | | |
1121 | 531 | for (cl = in; cl; cl = cl->next) { |
1122 | | |
1123 | 182 | b = NULL; |
1124 | | |
1125 | 2.37k | for ( ;; ) { |
1126 | | |
1127 | 2.37k | ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, |
1128 | 2.37k | "http body chunked buf " |
1129 | 2.37k | "t:%d f:%d %p, pos %p, size: %z file: %O, size: %O", |
1130 | 2.37k | cl->buf->temporary, cl->buf->in_file, |
1131 | 2.37k | cl->buf->start, cl->buf->pos, |
1132 | 2.37k | cl->buf->last - cl->buf->pos, |
1133 | 2.37k | cl->buf->file_pos, |
1134 | 2.37k | cl->buf->file_last - cl->buf->file_pos); |
1135 | | |
1136 | 2.37k | rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked, 0); |
1137 | | |
1138 | 2.37k | if (rc == NGX_OK) { |
1139 | | |
1140 | | /* a chunk has been parsed successfully */ |
1141 | | |
1142 | 2.18k | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
1143 | | |
1144 | 2.18k | if (clcf->client_max_body_size |
1145 | 2.18k | && clcf->client_max_body_size |
1146 | 2.18k | - r->headers_in.content_length_n < rb->chunked->size) |
1147 | 0 | { |
1148 | 0 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
1149 | 0 | "client intended to send too large chunked " |
1150 | 0 | "body: %O+%O bytes", |
1151 | 0 | r->headers_in.content_length_n, |
1152 | 0 | rb->chunked->size); |
1153 | |
|
1154 | 0 | r->lingering_close = 1; |
1155 | |
|
1156 | 0 | return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; |
1157 | 0 | } |
1158 | | |
1159 | 2.18k | if (b |
1160 | 2.06k | && rb->chunked->size <= 128 |
1161 | 1.93k | && cl->buf->last - cl->buf->pos >= rb->chunked->size) |
1162 | 1.92k | { |
1163 | 1.92k | r->headers_in.content_length_n += rb->chunked->size; |
1164 | | |
1165 | 1.92k | if (rb->chunked->size < 8) { |
1166 | | |
1167 | 15.4k | while (rb->chunked->size) { |
1168 | 13.4k | *b->last++ = *cl->buf->pos++; |
1169 | 13.4k | rb->chunked->size--; |
1170 | 13.4k | } |
1171 | | |
1172 | 1.92k | } else { |
1173 | 0 | ngx_memmove(b->last, cl->buf->pos, rb->chunked->size); |
1174 | 0 | b->last += rb->chunked->size; |
1175 | 0 | cl->buf->pos += rb->chunked->size; |
1176 | 0 | rb->chunked->size = 0; |
1177 | 0 | } |
1178 | | |
1179 | 1.92k | continue; |
1180 | 1.92k | } |
1181 | | |
1182 | 263 | tl = ngx_chain_get_free_buf(r->pool, &rb->free); |
1183 | 263 | if (tl == NULL) { |
1184 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
1185 | 0 | } |
1186 | | |
1187 | 263 | b = tl->buf; |
1188 | | |
1189 | 263 | ngx_memzero(b, sizeof(ngx_buf_t)); |
1190 | | |
1191 | 263 | b->temporary = 1; |
1192 | 263 | b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body; |
1193 | 263 | b->start = cl->buf->pos; |
1194 | 263 | b->pos = cl->buf->pos; |
1195 | 263 | b->last = cl->buf->last; |
1196 | 263 | b->end = cl->buf->end; |
1197 | 263 | b->flush = r->request_body_no_buffering; |
1198 | | |
1199 | 263 | *ll = tl; |
1200 | 263 | ll = &tl->next; |
1201 | | |
1202 | 263 | size = cl->buf->last - cl->buf->pos; |
1203 | | |
1204 | 263 | if ((off_t) size > rb->chunked->size) { |
1205 | 202 | cl->buf->pos += (size_t) rb->chunked->size; |
1206 | 202 | r->headers_in.content_length_n += rb->chunked->size; |
1207 | 202 | rb->chunked->size = 0; |
1208 | | |
1209 | 202 | } else { |
1210 | 61 | rb->chunked->size -= size; |
1211 | 61 | r->headers_in.content_length_n += size; |
1212 | 61 | cl->buf->pos = cl->buf->last; |
1213 | 61 | } |
1214 | | |
1215 | 263 | b->last = cl->buf->pos; |
1216 | | |
1217 | 263 | continue; |
1218 | 263 | } |
1219 | | |
1220 | 182 | if (rc == NGX_DONE) { |
1221 | | |
1222 | | /* a whole response has been parsed successfully */ |
1223 | |
|
1224 | 0 | rb->rest = 0; |
1225 | |
|
1226 | 0 | tl = ngx_chain_get_free_buf(r->pool, &rb->free); |
1227 | 0 | if (tl == NULL) { |
1228 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
1229 | 0 | } |
1230 | | |
1231 | 0 | b = tl->buf; |
1232 | |
|
1233 | 0 | ngx_memzero(b, sizeof(ngx_buf_t)); |
1234 | |
|
1235 | 0 | b->last_buf = 1; |
1236 | |
|
1237 | 0 | *ll = tl; |
1238 | 0 | ll = &tl->next; |
1239 | |
|
1240 | 0 | break; |
1241 | 0 | } |
1242 | | |
1243 | 182 | if (rc == NGX_AGAIN) { |
1244 | | |
1245 | | /* set rb->rest, amount of data we want to see next time */ |
1246 | | |
1247 | 179 | cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); |
1248 | | |
1249 | 179 | rb->rest = ngx_max(rb->chunked->length, |
1250 | 179 | (off_t) cscf->large_client_header_buffers.size); |
1251 | | |
1252 | 179 | break; |
1253 | 179 | } |
1254 | | |
1255 | | /* invalid */ |
1256 | | |
1257 | 3 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
1258 | 3 | "client sent invalid chunked body"); |
1259 | | |
1260 | 3 | return NGX_HTTP_BAD_REQUEST; |
1261 | 182 | } |
1262 | 182 | } |
1263 | | |
1264 | 349 | rc = ngx_http_top_request_body_filter(r, out); |
1265 | | |
1266 | 349 | ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out, |
1267 | 349 | (ngx_buf_tag_t) &ngx_http_read_client_request_body); |
1268 | | |
1269 | 349 | return rc; |
1270 | 352 | } |
1271 | | |
1272 | | |
1273 | | ngx_int_t |
1274 | | ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in) |
1275 | 547 | { |
1276 | 547 | ngx_buf_t *b; |
1277 | 547 | ngx_chain_t *cl, *tl, **ll; |
1278 | 547 | ngx_http_request_body_t *rb; |
1279 | | |
1280 | 547 | rb = r->request_body; |
1281 | | |
1282 | 547 | ll = &rb->bufs; |
1283 | | |
1284 | 551 | for (cl = rb->bufs; cl; cl = cl->next) { |
1285 | | |
1286 | | #if 0 |
1287 | | ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, |
1288 | | "http body old buf t:%d f:%d %p, pos %p, size: %z " |
1289 | | "file: %O, size: %O", |
1290 | | cl->buf->temporary, cl->buf->in_file, |
1291 | | cl->buf->start, cl->buf->pos, |
1292 | | cl->buf->last - cl->buf->pos, |
1293 | | cl->buf->file_pos, |
1294 | | cl->buf->file_last - cl->buf->file_pos); |
1295 | | #endif |
1296 | | |
1297 | 4 | ll = &cl->next; |
1298 | 4 | } |
1299 | | |
1300 | 908 | for (cl = in; cl; cl = cl->next) { |
1301 | | |
1302 | 361 | ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, |
1303 | 361 | "http body new buf t:%d f:%d %p, pos %p, size: %z " |
1304 | 361 | "file: %O, size: %O", |
1305 | 361 | cl->buf->temporary, cl->buf->in_file, |
1306 | 361 | cl->buf->start, cl->buf->pos, |
1307 | 361 | cl->buf->last - cl->buf->pos, |
1308 | 361 | cl->buf->file_pos, |
1309 | 361 | cl->buf->file_last - cl->buf->file_pos); |
1310 | | |
1311 | 361 | if (cl->buf->last_buf) { |
1312 | |
|
1313 | 0 | if (rb->last_saved) { |
1314 | 0 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, |
1315 | 0 | "duplicate last buf in save filter"); |
1316 | 0 | *ll = NULL; |
1317 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
1318 | 0 | } |
1319 | | |
1320 | 0 | rb->last_saved = 1; |
1321 | 0 | } |
1322 | | |
1323 | 361 | tl = ngx_alloc_chain_link(r->pool); |
1324 | 361 | if (tl == NULL) { |
1325 | 0 | *ll = NULL; |
1326 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
1327 | 0 | } |
1328 | | |
1329 | 361 | tl->buf = cl->buf; |
1330 | 361 | *ll = tl; |
1331 | 361 | ll = &tl->next; |
1332 | 361 | } |
1333 | | |
1334 | 547 | *ll = NULL; |
1335 | | |
1336 | 547 | if (r->request_body_no_buffering) { |
1337 | 0 | return NGX_OK; |
1338 | 0 | } |
1339 | | |
1340 | 547 | if (rb->rest > 0) { |
1341 | | |
1342 | 547 | if (rb->bufs && rb->buf && rb->buf->last == rb->buf->end |
1343 | 214 | && ngx_http_write_request_body(r) != NGX_OK) |
1344 | 0 | { |
1345 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
1346 | 0 | } |
1347 | | |
1348 | 547 | return NGX_OK; |
1349 | 547 | } |
1350 | | |
1351 | 0 | if (!rb->last_saved) { |
1352 | 0 | return NGX_OK; |
1353 | 0 | } |
1354 | | |
1355 | 0 | if (rb->temp_file || r->request_body_in_file_only) { |
1356 | |
|
1357 | 0 | if (rb->bufs && rb->bufs->buf->in_file) { |
1358 | 0 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, |
1359 | 0 | "body already in file"); |
1360 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
1361 | 0 | } |
1362 | | |
1363 | 0 | if (ngx_http_write_request_body(r) != NGX_OK) { |
1364 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
1365 | 0 | } |
1366 | | |
1367 | 0 | if (rb->temp_file->file.offset != 0) { |
1368 | |
|
1369 | 0 | cl = ngx_chain_get_free_buf(r->pool, &rb->free); |
1370 | 0 | if (cl == NULL) { |
1371 | 0 | return NGX_HTTP_INTERNAL_SERVER_ERROR; |
1372 | 0 | } |
1373 | | |
1374 | 0 | b = cl->buf; |
1375 | |
|
1376 | 0 | ngx_memzero(b, sizeof(ngx_buf_t)); |
1377 | |
|
1378 | 0 | b->in_file = 1; |
1379 | 0 | b->file_last = rb->temp_file->file.offset; |
1380 | 0 | b->file = &rb->temp_file->file; |
1381 | |
|
1382 | 0 | rb->bufs = cl; |
1383 | 0 | } |
1384 | 0 | } |
1385 | | |
1386 | 0 | return NGX_OK; |
1387 | 0 | } |