Lines 266-315
typedef struct {
Link Here
|
266 |
prefetch_nonblocking:1; |
266 |
prefetch_nonblocking:1; |
267 |
} proxy_http_req_t; |
267 |
} proxy_http_req_t; |
268 |
|
268 |
|
269 |
/* Read what's in the client pipe. If nonblocking is set and read is EAGAIN, |
|
|
270 |
* pass a FLUSH bucket to the backend and read again in blocking mode. |
271 |
*/ |
272 |
static int stream_reqbody_read(proxy_http_req_t *req, apr_bucket_brigade *bb, |
273 |
int nonblocking) |
274 |
{ |
275 |
request_rec *r = req->r; |
276 |
proxy_conn_rec *p_conn = req->backend; |
277 |
apr_bucket_alloc_t *bucket_alloc = req->bucket_alloc; |
278 |
apr_read_type_e block = nonblocking ? APR_NONBLOCK_READ : APR_BLOCK_READ; |
279 |
apr_status_t status; |
280 |
int rv; |
281 |
|
282 |
for (;;) { |
283 |
status = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, |
284 |
block, HUGE_STRING_LEN); |
285 |
if (block == APR_BLOCK_READ |
286 |
|| (!APR_STATUS_IS_EAGAIN(status) |
287 |
&& (status != APR_SUCCESS || !APR_BRIGADE_EMPTY(bb)))) { |
288 |
break; |
289 |
} |
290 |
|
291 |
/* Flush and retry (blocking) */ |
292 |
apr_brigade_cleanup(bb); |
293 |
rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, req->origin, bb, 1); |
294 |
if (rv != OK) { |
295 |
return rv; |
296 |
} |
297 |
block = APR_BLOCK_READ; |
298 |
} |
299 |
|
300 |
if (status != APR_SUCCESS) { |
301 |
conn_rec *c = r->connection; |
302 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02608) |
303 |
"read request body failed to %pI (%s)" |
304 |
" from %s (%s)", p_conn->addr, |
305 |
p_conn->hostname ? p_conn->hostname: "", |
306 |
c->client_ip, c->remote_host ? c->remote_host: ""); |
307 |
return ap_map_http_request_error(status, HTTP_BAD_REQUEST); |
308 |
} |
309 |
|
310 |
return OK; |
311 |
} |
312 |
|
313 |
static int stream_reqbody(proxy_http_req_t *req) |
269 |
static int stream_reqbody(proxy_http_req_t *req) |
314 |
{ |
270 |
{ |
315 |
request_rec *r = req->r; |
271 |
request_rec *r = req->r; |
Lines 328-334
static int stream_reqbody(proxy_http_req_t *req)
Link Here
|
328 |
do { |
284 |
do { |
329 |
if (APR_BRIGADE_EMPTY(input_brigade) |
285 |
if (APR_BRIGADE_EMPTY(input_brigade) |
330 |
&& APR_BRIGADE_EMPTY(header_brigade)) { |
286 |
&& APR_BRIGADE_EMPTY(header_brigade)) { |
331 |
rv = stream_reqbody_read(req, input_brigade, 1); |
287 |
rv = ap_proxy_read_input(r, p_conn, input_brigade, |
|
|
288 |
HUGE_STRING_LEN); |
332 |
if (rv != OK) { |
289 |
if (rv != OK) { |
333 |
return rv; |
290 |
return rv; |
334 |
} |
291 |
} |
Lines 409-415
static int stream_reqbody(proxy_http_req_t *req)
Link Here
|
409 |
*/ |
366 |
*/ |
410 |
APR_BRIGADE_PREPEND(input_brigade, header_brigade); |
367 |
APR_BRIGADE_PREPEND(input_brigade, header_brigade); |
411 |
|
368 |
|
412 |
/* Flush here on EOS because we won't stream_reqbody_read() again */ |
369 |
/* Flush here on EOS because we won't ap_proxy_read_input() again. */ |
413 |
rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin, |
370 |
rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin, |
414 |
input_brigade, seen_eos); |
371 |
input_brigade, seen_eos); |
415 |
if (rv != OK) { |
372 |
if (rv != OK) { |
Lines 427-564
static int stream_reqbody(proxy_http_req_t *req)
Link Here
|
427 |
return OK; |
384 |
return OK; |
428 |
} |
385 |
} |
429 |
|
386 |
|
430 |
static int spool_reqbody_cl(proxy_http_req_t *req, apr_off_t *bytes_spooled) |
|
|
431 |
{ |
432 |
apr_pool_t *p = req->p; |
433 |
request_rec *r = req->r; |
434 |
int seen_eos = 0, rv = OK; |
435 |
apr_status_t status = APR_SUCCESS; |
436 |
apr_bucket_alloc_t *bucket_alloc = req->bucket_alloc; |
437 |
apr_bucket_brigade *input_brigade = req->input_brigade; |
438 |
apr_bucket_brigade *body_brigade; |
439 |
apr_bucket *e; |
440 |
apr_off_t bytes, fsize = 0; |
441 |
apr_file_t *tmpfile = NULL; |
442 |
apr_off_t limit; |
443 |
|
444 |
body_brigade = apr_brigade_create(p, bucket_alloc); |
445 |
*bytes_spooled = 0; |
446 |
|
447 |
limit = ap_get_limit_req_body(r); |
448 |
|
449 |
do { |
450 |
if (APR_BRIGADE_EMPTY(input_brigade)) { |
451 |
rv = stream_reqbody_read(req, input_brigade, 0); |
452 |
if (rv != OK) { |
453 |
return rv; |
454 |
} |
455 |
} |
456 |
|
457 |
/* If this brigade contains EOS, either stop or remove it. */ |
458 |
if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) { |
459 |
seen_eos = 1; |
460 |
} |
461 |
|
462 |
apr_brigade_length(input_brigade, 1, &bytes); |
463 |
|
464 |
if (*bytes_spooled + bytes > MAX_MEM_SPOOL) { |
465 |
/* |
466 |
* LimitRequestBody does not affect Proxy requests (Should it?). |
467 |
* Let it take effect if we decide to store the body in a |
468 |
* temporary file on disk. |
469 |
*/ |
470 |
if (limit && (*bytes_spooled + bytes > limit)) { |
471 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01088) |
472 |
"Request body is larger than the configured " |
473 |
"limit of %" APR_OFF_T_FMT, limit); |
474 |
return HTTP_REQUEST_ENTITY_TOO_LARGE; |
475 |
} |
476 |
/* can't spool any more in memory; write latest brigade to disk */ |
477 |
if (tmpfile == NULL) { |
478 |
const char *temp_dir; |
479 |
char *template; |
480 |
|
481 |
status = apr_temp_dir_get(&temp_dir, p); |
482 |
if (status != APR_SUCCESS) { |
483 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01089) |
484 |
"search for temporary directory failed"); |
485 |
return HTTP_INTERNAL_SERVER_ERROR; |
486 |
} |
487 |
apr_filepath_merge(&template, temp_dir, |
488 |
"modproxy.tmp.XXXXXX", |
489 |
APR_FILEPATH_NATIVE, p); |
490 |
status = apr_file_mktemp(&tmpfile, template, 0, p); |
491 |
if (status != APR_SUCCESS) { |
492 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01090) |
493 |
"creation of temporary file in directory " |
494 |
"%s failed", temp_dir); |
495 |
return HTTP_INTERNAL_SERVER_ERROR; |
496 |
} |
497 |
} |
498 |
for (e = APR_BRIGADE_FIRST(input_brigade); |
499 |
e != APR_BRIGADE_SENTINEL(input_brigade); |
500 |
e = APR_BUCKET_NEXT(e)) { |
501 |
const char *data; |
502 |
apr_size_t bytes_read, bytes_written; |
503 |
|
504 |
apr_bucket_read(e, &data, &bytes_read, APR_BLOCK_READ); |
505 |
status = apr_file_write_full(tmpfile, data, bytes_read, &bytes_written); |
506 |
if (status != APR_SUCCESS) { |
507 |
const char *tmpfile_name; |
508 |
|
509 |
if (apr_file_name_get(&tmpfile_name, tmpfile) != APR_SUCCESS) { |
510 |
tmpfile_name = "(unknown)"; |
511 |
} |
512 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01091) |
513 |
"write to temporary file %s failed", |
514 |
tmpfile_name); |
515 |
return HTTP_INTERNAL_SERVER_ERROR; |
516 |
} |
517 |
AP_DEBUG_ASSERT(bytes_read == bytes_written); |
518 |
fsize += bytes_written; |
519 |
} |
520 |
apr_brigade_cleanup(input_brigade); |
521 |
} |
522 |
else { |
523 |
|
524 |
/* |
525 |
* Save input_brigade in body_brigade. (At least) in the SSL case |
526 |
* input_brigade contains transient buckets whose data would get |
527 |
* overwritten during the next call of ap_get_brigade in the loop. |
528 |
* ap_save_brigade ensures these buckets to be set aside. |
529 |
* Calling ap_save_brigade with NULL as filter is OK, because |
530 |
* body_brigade already has been created and does not need to get |
531 |
* created by ap_save_brigade. |
532 |
*/ |
533 |
status = ap_save_brigade(NULL, &body_brigade, &input_brigade, p); |
534 |
if (status != APR_SUCCESS) { |
535 |
return HTTP_INTERNAL_SERVER_ERROR; |
536 |
} |
537 |
|
538 |
} |
539 |
|
540 |
*bytes_spooled += bytes; |
541 |
} while (!seen_eos); |
542 |
|
543 |
APR_BRIGADE_CONCAT(input_brigade, body_brigade); |
544 |
if (tmpfile) { |
545 |
apr_brigade_insert_file(input_brigade, tmpfile, 0, fsize, p); |
546 |
} |
547 |
if (apr_table_get(r->subprocess_env, "proxy-sendextracrlf")) { |
548 |
e = apr_bucket_immortal_create(CRLF_ASCII, 2, bucket_alloc); |
549 |
APR_BRIGADE_INSERT_TAIL(input_brigade, e); |
550 |
} |
551 |
if (tmpfile) { |
552 |
/* We dropped metadata buckets when spooling to tmpfile, |
553 |
* terminate with EOS for stream_reqbody() to flush the |
554 |
* whole in one go. |
555 |
*/ |
556 |
e = apr_bucket_eos_create(bucket_alloc); |
557 |
APR_BRIGADE_INSERT_TAIL(input_brigade, e); |
558 |
} |
559 |
return OK; |
560 |
} |
561 |
|
562 |
static int ap_proxy_http_prefetch(proxy_http_req_t *req, |
387 |
static int ap_proxy_http_prefetch(proxy_http_req_t *req, |
563 |
apr_uri_t *uri, char *url) |
388 |
apr_uri_t *uri, char *url) |
564 |
{ |
389 |
{ |
Lines 569-582
static int ap_proxy_http_prefetch(proxy_http_req_t
Link Here
|
569 |
apr_bucket_alloc_t *bucket_alloc = req->bucket_alloc; |
394 |
apr_bucket_alloc_t *bucket_alloc = req->bucket_alloc; |
570 |
apr_bucket_brigade *header_brigade = req->header_brigade; |
395 |
apr_bucket_brigade *header_brigade = req->header_brigade; |
571 |
apr_bucket_brigade *input_brigade = req->input_brigade; |
396 |
apr_bucket_brigade *input_brigade = req->input_brigade; |
572 |
apr_bucket_brigade *temp_brigade; |
|
|
573 |
apr_bucket *e; |
397 |
apr_bucket *e; |
574 |
char *buf; |
398 |
char *buf; |
575 |
apr_status_t status; |
|
|
576 |
apr_off_t bytes_read = 0; |
399 |
apr_off_t bytes_read = 0; |
577 |
apr_off_t bytes; |
400 |
apr_off_t bytes; |
578 |
int force10, rv; |
401 |
int force10, rv; |
579 |
apr_read_type_e block; |
|
|
580 |
conn_rec *origin = p_conn->connection; |
402 |
conn_rec *origin = p_conn->connection; |
581 |
|
403 |
|
582 |
if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) { |
404 |
if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) { |
Lines 641-709
static int ap_proxy_http_prefetch(proxy_http_req_t
Link Here
|
641 |
p_conn->close = 1; |
463 |
p_conn->close = 1; |
642 |
} |
464 |
} |
643 |
|
465 |
|
644 |
/* Prefetch MAX_MEM_SPOOL bytes |
466 |
rv = ap_proxy_prefetch_input(r, req->backend, input_brigade, |
645 |
* |
467 |
req->prefetch_nonblocking ? APR_NONBLOCK_READ |
646 |
* This helps us avoid any election of C-L v.s. T-E |
468 |
: APR_BLOCK_READ, |
647 |
* request bodies, since we are willing to keep in |
469 |
&bytes_read, MAX_MEM_SPOOL); |
648 |
* memory this much data, in any case. This gives |
470 |
if (rv != OK) { |
649 |
* us an instant C-L election if the body is of some |
471 |
return rv; |
650 |
* reasonable size. |
|
|
651 |
*/ |
652 |
temp_brigade = apr_brigade_create(p, bucket_alloc); |
653 |
block = req->prefetch_nonblocking ? APR_NONBLOCK_READ : APR_BLOCK_READ; |
654 |
|
655 |
/* Account for saved input, if any. */ |
656 |
apr_brigade_length(input_brigade, 0, &bytes_read); |
657 |
|
658 |
/* Ensure we don't hit a wall where we have a buffer too small |
659 |
* for ap_get_brigade's filters to fetch us another bucket, |
660 |
* surrender once we hit 80 bytes less than MAX_MEM_SPOOL |
661 |
* (an arbitrary value). |
662 |
*/ |
663 |
while (bytes_read < MAX_MEM_SPOOL - 80 |
664 |
&& (APR_BRIGADE_EMPTY(input_brigade) |
665 |
|| !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade)))) { |
666 |
status = ap_get_brigade(r->input_filters, temp_brigade, |
667 |
AP_MODE_READBYTES, block, |
668 |
MAX_MEM_SPOOL - bytes_read); |
669 |
/* ap_get_brigade may return success with an empty brigade |
670 |
* for a non-blocking read which would block |
671 |
*/ |
672 |
if (block == APR_NONBLOCK_READ |
673 |
&& ((status == APR_SUCCESS && APR_BRIGADE_EMPTY(temp_brigade)) |
674 |
|| APR_STATUS_IS_EAGAIN(status))) { |
675 |
break; |
676 |
} |
677 |
if (status != APR_SUCCESS) { |
678 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01095) |
679 |
"prefetch request body failed to %pI (%s)" |
680 |
" from %s (%s)", |
681 |
p_conn->addr, p_conn->hostname ? p_conn->hostname: "", |
682 |
c->client_ip, c->remote_host ? c->remote_host: ""); |
683 |
return ap_map_http_request_error(status, HTTP_BAD_REQUEST); |
684 |
} |
685 |
|
686 |
apr_brigade_length(temp_brigade, 1, &bytes); |
687 |
bytes_read += bytes; |
688 |
|
689 |
/* |
690 |
* Save temp_brigade in input_brigade. (At least) in the SSL case |
691 |
* temp_brigade contains transient buckets whose data would get |
692 |
* overwritten during the next call of ap_get_brigade in the loop. |
693 |
* ap_save_brigade ensures these buckets to be set aside. |
694 |
* Calling ap_save_brigade with NULL as filter is OK, because |
695 |
* input_brigade already has been created and does not need to get |
696 |
* created by ap_save_brigade. |
697 |
*/ |
698 |
status = ap_save_brigade(NULL, &input_brigade, &temp_brigade, p); |
699 |
if (status != APR_SUCCESS) { |
700 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01096) |
701 |
"processing prefetched request body failed" |
702 |
" to %pI (%s) from %s (%s)", |
703 |
p_conn->addr, p_conn->hostname ? p_conn->hostname: "", |
704 |
c->client_ip, c->remote_host ? c->remote_host: ""); |
705 |
return HTTP_INTERNAL_SERVER_ERROR; |
706 |
} |
707 |
} |
472 |
} |
708 |
|
473 |
|
709 |
/* Use chunked request body encoding or send a content-length body? |
474 |
/* Use chunked request body encoding or send a content-length body? |
Lines 770-776
static int ap_proxy_http_prefetch(proxy_http_req_t
Link Here
|
770 |
else if (req->old_cl_val) { |
535 |
else if (req->old_cl_val) { |
771 |
if (r->input_filters == r->proto_input_filters) { |
536 |
if (r->input_filters == r->proto_input_filters) { |
772 |
if (!ap_parse_strict_length(&req->cl_val, req->old_cl_val)) { |
537 |
if (!ap_parse_strict_length(&req->cl_val, req->old_cl_val)) { |
773 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01085) |
538 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01085) |
774 |
"could not parse request Content-Length (%s)", |
539 |
"could not parse request Content-Length (%s)", |
775 |
req->old_cl_val); |
540 |
req->old_cl_val); |
776 |
return HTTP_BAD_REQUEST; |
541 |
return HTTP_BAD_REQUEST; |
Lines 810-816
static int ap_proxy_http_prefetch(proxy_http_req_t
Link Here
|
810 |
/* If we have to spool the body, do it now, before connecting or |
575 |
/* If we have to spool the body, do it now, before connecting or |
811 |
* reusing the backend connection. |
576 |
* reusing the backend connection. |
812 |
*/ |
577 |
*/ |
813 |
rv = spool_reqbody_cl(req, &bytes); |
578 |
rv = ap_proxy_spool_input(r, p_conn, input_brigade, |
|
|
579 |
&bytes, MAX_MEM_SPOOL); |
814 |
if (rv != OK) { |
580 |
if (rv != OK) { |
815 |
return rv; |
581 |
return rv; |
816 |
} |
582 |
} |