View | Details | Raw Unified | Return to bug 57087
Collapse All | Expand All

(-)modules/proxy/mod_proxy.h (+49 lines)
Lines 1168-1173 PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_poo Link Here
1168
                                           char **old_te_val);
1168
                                           char **old_te_val);
1169
1169
1170
/**
1170
/**
1171
 * Prefetch the client request body (in memory), up to a limit.
1172
 * Read what's in the client pipe. If nonblocking is set and read is EAGAIN,
1173
 * pass a FLUSH bucket to the backend and read again in blocking mode.
1174
 * @param r             client request
1175
 * @param backend       backend connection
1176
 * @param input_brigade input brigade to use/fill
1177
 * @param block         blocking or non-blocking mode
1178
 * @param bytes_read    number of bytes read
1179
 * @param max_read      maximum number of bytes to read
1180
 * @return              OK or HTTP_* error code
1181
 * @note max_read is rounded up to APR_BUCKET_BUFF_SIZE
1182
 */
1183
PROXY_DECLARE(int) ap_proxy_prefetch_input(request_rec *r,
1184
                                           proxy_conn_rec *backend,
1185
                                           apr_bucket_brigade *input_brigade,
1186
                                           apr_read_type_e block,
1187
                                           apr_off_t *bytes_read,
1188
                                           apr_off_t max_read);
1189
1190
/**
1191
 * Spool the client request body to memory, or disk above given limit.
1192
 * @param r             client request
1193
 * @param backend       backend connection
1194
 * @param input_brigade input brigade to use/fill
1195
 * @param bytes_spooled number of bytes spooled
1196
 * @param max_mem_spool maximum number of in-memory bytes
1197
 * @return              OK or HTTP_* error code
1198
 */
1199
PROXY_DECLARE(int) ap_proxy_spool_input(request_rec *r,
1200
                                        proxy_conn_rec *backend,
1201
                                        apr_bucket_brigade *input_brigade,
1202
                                        apr_off_t *bytes_spooled,
1203
                                        apr_off_t max_mem_spool);
1204
1205
/**
1206
 * Read what's in the client pipe. If the read would block (EAGAIN),
1207
 * pass a FLUSH bucket to the backend and read again in blocking mode.
1208
 * @param r             client request
1209
 * @param backend       backend connection
1210
 * @param input_brigade brigade to use/fill
1211
 * @param max_read      maximum number of bytes to read
1212
 * @return              OK or HTTP_* error code
1213
 */
1214
PROXY_DECLARE(int) ap_proxy_read_input(request_rec *r,
1215
                                       proxy_conn_rec *backend,
1216
                                       apr_bucket_brigade *input_brigade,
1217
                                       apr_off_t max_read);
1218
1219
/**
1171
 * @param bucket_alloc  bucket allocator
1220
 * @param bucket_alloc  bucket allocator
1172
 * @param r             request
1221
 * @param r             request
1173
 * @param p_conn        proxy connection
1222
 * @param p_conn        proxy connection
(-)modules/proxy/mod_proxy_fcgi.c (-7 / +126 lines)
Lines 532-538 static int handle_headers(request_rec *r, int *sta Link Here
532
static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
532
static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
533
                             request_rec *r, apr_pool_t *setaside_pool,
533
                             request_rec *r, apr_pool_t *setaside_pool,
534
                             apr_uint16_t request_id, const char **err,
534
                             apr_uint16_t request_id, const char **err,
535
                             int *bad_request, int *has_responded)
535
                             int *bad_request, int *has_responded,
536
                             apr_bucket_brigade *input_brigade)
536
{
537
{
537
    apr_bucket_brigade *ib, *ob;
538
    apr_bucket_brigade *ib, *ob;
538
    int seen_end_of_headers = 0, done = 0, ignore_body = 0;
539
    int seen_end_of_headers = 0, done = 0, ignore_body = 0;
Lines 594-602 static apr_status_t dispatch(proxy_conn_rec *conn, Link Here
594
            int last_stdin = 0;
595
            int last_stdin = 0;
595
            char *iobuf_cursor;
596
            char *iobuf_cursor;
596
597
597
            rv = ap_get_brigade(r->input_filters, ib,
598
            if (APR_BRIGADE_EMPTY(input_brigade)) {
598
                                AP_MODE_READBYTES, APR_BLOCK_READ,
599
                rv = ap_get_brigade(r->input_filters, ib,
599
                                iobuf_size);
600
                                    AP_MODE_READBYTES, APR_BLOCK_READ,
601
                                    iobuf_size);
602
            }
603
            else {
604
                apr_bucket *e;
605
                APR_BRIGADE_CONCAT(ib, input_brigade);
606
                rv = apr_brigade_partition(ib, iobuf_size, &e);
607
                if (rv == APR_SUCCESS) {
608
                    while (e != APR_BRIGADE_SENTINEL(ib)
609
                           && APR_BUCKET_IS_METADATA(e)) {
610
                        e = APR_BUCKET_NEXT(e);
611
                    }
612
                    apr_brigade_split_ex(ib, e, input_brigade);
613
                }
614
                else if (rv == APR_INCOMPLETE) {
615
                    rv = APR_SUCCESS;
616
                }
617
            }
600
            if (rv != APR_SUCCESS) {
618
            if (rv != APR_SUCCESS) {
601
                *err = "reading input brigade";
619
                *err = "reading input brigade";
602
                *bad_request = 1;
620
                *bad_request = 1;
Lines 935-941 static int fcgi_do_request(apr_pool_t *p, request_ Link Here
935
                           conn_rec *origin,
953
                           conn_rec *origin,
936
                           proxy_dir_conf *conf,
954
                           proxy_dir_conf *conf,
937
                           apr_uri_t *uri,
955
                           apr_uri_t *uri,
938
                           char *url, char *server_portstr)
956
                           char *url, char *server_portstr,
957
                           apr_bucket_brigade *input_brigade)
939
{
958
{
940
    /* Request IDs are arbitrary numbers that we assign to a
959
    /* Request IDs are arbitrary numbers that we assign to a
941
     * single request. This would allow multiplex/pipelining of
960
     * single request. This would allow multiplex/pipelining of
Lines 971-977 static int fcgi_do_request(apr_pool_t *p, request_ Link Here
971
990
972
    /* Step 3: Read records from the back end server and handle them. */
991
    /* Step 3: Read records from the back end server and handle them. */
973
    rv = dispatch(conn, conf, r, temp_pool, request_id,
992
    rv = dispatch(conn, conf, r, temp_pool, request_id,
974
                  &err, &bad_request, &has_responded);
993
                  &err, &bad_request, &has_responded,
994
                  input_brigade);
975
    if (rv != APR_SUCCESS) {
995
    if (rv != APR_SUCCESS) {
976
        /* If the client aborted the connection during retrieval or (partially)
996
        /* If the client aborted the connection during retrieval or (partially)
977
         * sending the response, don't return a HTTP_SERVICE_UNAVAILABLE, since
997
         * sending the response, don't return a HTTP_SERVICE_UNAVAILABLE, since
Lines 1007-1012 static int fcgi_do_request(apr_pool_t *p, request_ Link Here
1007
1027
1008
#define FCGI_SCHEME "FCGI"
1028
#define FCGI_SCHEME "FCGI"
1009
1029
1030
#define MAX_MEM_SPOOL 16384
1031
1010
/*
1032
/*
1011
 * This handles fcgi:(dest) URLs
1033
 * This handles fcgi:(dest) URLs
1012
 */
1034
 */
Lines 1019-1024 static int proxy_fcgi_handler(request_rec *r, prox Link Here
1019
    char server_portstr[32];
1041
    char server_portstr[32];
1020
    conn_rec *origin = NULL;
1042
    conn_rec *origin = NULL;
1021
    proxy_conn_rec *backend = NULL;
1043
    proxy_conn_rec *backend = NULL;
1044
    apr_bucket_brigade *input_brigade;
1045
    apr_off_t input_bytes = 0;
1022
    apr_uri_t *uri;
1046
    apr_uri_t *uri;
1023
1047
1024
    proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
1048
    proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
Lines 1061-1066 static int proxy_fcgi_handler(request_rec *r, prox Link Here
1061
        goto cleanup;
1085
        goto cleanup;
1062
    }
1086
    }
1063
1087
1088
    /* We possibly reuse input data prefetched in previous call(s), e.g. for a
1089
     * balancer fallback scenario.
1090
     */
1091
    apr_pool_userdata_get((void **)&input_brigade, "proxy-fcgi-input", p);
1092
    if (input_brigade == NULL) {
1093
        const char *old_te = apr_table_get(r->headers_in, "Transfer-Encoding");
1094
        const char *old_cl = NULL;
1095
        if (old_te) {
1096
            apr_table_unset(r->headers_in, "Content-Length");
1097
        }
1098
        else {
1099
            old_cl = apr_table_get(r->headers_in, "Content-Length");
1100
        }
1101
1102
        input_brigade = apr_brigade_create(p, r->connection->bucket_alloc);
1103
        apr_pool_userdata_setn(input_brigade, "proxy-fcgi-input", NULL, p);
1104
1105
        /* Prefetch (nonlocking) the request body so to increase the chance
1106
         * to get the whole (or enough) body and determine Content-Length vs
1107
         * chunked or spooled. By doing this before connecting or reusing the
1108
         * backend, we want to minimize the delay between this connection is
1109
         * considered alive and the first bytes sent (should the client's link
1110
         * be slow or some input filter retain the data). This is a best effort
1111
         * to prevent the backend from closing (from under us) what it thinks is
1112
         * an idle connection, hence to reduce to the minimum the unavoidable
1113
         * local is_socket_connected() vs remote keepalive race condition.
1114
         */
1115
        status = ap_proxy_prefetch_input(r, backend, input_brigade,
1116
                                         APR_NONBLOCK_READ, &input_bytes,
1117
                                         MAX_MEM_SPOOL);
1118
        if (status != OK) {
1119
            goto cleanup;
1120
        }
1121
1122
        /*
1123
         * The request body is streamed by default, using either C-L or
1124
         * chunked T-E, like this:
1125
         *
1126
         *   The whole body (including no body) was received on prefetch, i.e.
1127
         *   the input brigade ends with EOS => C-L = input_bytes.
1128
         *
1129
         *   C-L is known and reliable, i.e. only protocol filters in the input
1130
         *   chain thus none should change the body => use C-L from client.
1131
         *
1132
         *   The administrator has not "proxy-sendcl" which prevents T-E => use
1133
         *   T-E and chunks.
1134
         *
1135
         *   Otherwise we need to determine and set a content-length, so spool
1136
         *   the entire request body to memory/temporary file (MAX_MEM_SPOOL),
1137
         *   such that we finally know its length => C-L = input_bytes.
1138
         */
1139
        if (!APR_BRIGADE_EMPTY(input_brigade)
1140
                && APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
1141
            /* The whole thing fit, so our decision is trivial, use the input
1142
             * bytes for the Content-Length. If we expected no body, and read
1143
             * no body, do not set the Content-Length.
1144
             */
1145
            if (old_cl || old_te || input_bytes) {
1146
                apr_table_setn(r->headers_in, "Content-Length",
1147
                               apr_off_t_toa(p, input_bytes));
1148
                if (old_te) {
1149
                    apr_table_unset(r->headers_in, "Transfer-Encoding");
1150
                }
1151
            }
1152
        }
1153
        else if (old_cl && r->input_filters == r->proto_input_filters) {
1154
            /* Streaming is possible by preserving the existing C-L */
1155
        }
1156
        else if (!apr_table_get(r->subprocess_env, "proxy-sendcl")) {
1157
            /* Streaming is possible using T-E: chunked */
1158
        }
1159
        else {
1160
            /* No streaming, C-L is the only option so spool to memory/file */
1161
            apr_bucket_brigade *tmp_bb;
1162
            apr_off_t remaining_bytes = 0;
1163
1164
            AP_DEBUG_ASSERT(MAX_MEM_SPOOL >= input_bytes);
1165
            tmp_bb = apr_brigade_create(p, r->connection->bucket_alloc);
1166
            status = ap_proxy_spool_input(r, backend, tmp_bb, &remaining_bytes,
1167
                                          MAX_MEM_SPOOL - input_bytes);
1168
            if (status != OK) {
1169
                goto cleanup;
1170
            }
1171
1172
            APR_BRIGADE_CONCAT(input_brigade, tmp_bb);
1173
            input_bytes += remaining_bytes;
1174
1175
            apr_table_setn(r->headers_in, "Content-Length",
1176
                           apr_off_t_toa(p, input_bytes));
1177
            if (old_te) {
1178
                apr_table_unset(r->headers_in, "Transfer-Encoding");
1179
            }
1180
        }
1181
    }
1182
1064
    /* This scheme handler does not reuse connections by default, to
1183
    /* This scheme handler does not reuse connections by default, to
1065
     * avoid tying up a fastcgi that isn't expecting to work on
1184
     * avoid tying up a fastcgi that isn't expecting to work on
1066
     * parallel requests.  But if the user went out of their way to
1185
     * parallel requests.  But if the user went out of their way to
Lines 1085-1091 static int proxy_fcgi_handler(request_rec *r, prox Link Here
1085
1204
1086
    /* Step Three: Process the Request */
1205
    /* Step Three: Process the Request */
1087
    status = fcgi_do_request(p, r, backend, origin, dconf, uri, url,
1206
    status = fcgi_do_request(p, r, backend, origin, dconf, uri, url,
1088
                             server_portstr);
1207
                             server_portstr, input_brigade);
1089
1208
1090
cleanup:
1209
cleanup:
1091
    ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
1210
    ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
(-)modules/proxy/mod_proxy_http.c (-246 / +12 lines)
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
        }
(-)modules/proxy/proxy_util.c (+262 lines)
Lines 3874-3879 PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_poo Link Here
3874
    return OK;
3874
    return OK;
3875
}
3875
}
3876
3876
3877
PROXY_DECLARE(int) ap_proxy_prefetch_input(request_rec *r,
3878
                                           proxy_conn_rec *backend,
3879
                                           apr_bucket_brigade *input_brigade,
3880
                                           apr_read_type_e block,
3881
                                           apr_off_t *bytes_read,
3882
                                           apr_off_t max_read)
3883
{
3884
    apr_pool_t *p = r->pool;
3885
    conn_rec *c = r->connection;
3886
    apr_bucket_brigade *temp_brigade;
3887
    apr_status_t status;
3888
    apr_off_t bytes;
3889
3890
    *bytes_read = 0;
3891
    if (max_read < APR_BUCKET_BUFF_SIZE) {
3892
        max_read = APR_BUCKET_BUFF_SIZE;
3893
    }
3894
3895
    /* Prefetch max_read bytes
3896
     *
3897
     * This helps us avoid any election of C-L v.s. T-E
3898
     * request bodies, since we are willing to keep in
3899
     * memory this much data, in any case.  This gives
3900
     * us an instant C-L election if the body is of some
3901
     * reasonable size.
3902
     */
3903
    temp_brigade = apr_brigade_create(p, input_brigade->bucket_alloc);
3904
3905
    /* Account for saved input, if any. */
3906
    apr_brigade_length(input_brigade, 0, bytes_read);
3907
3908
    /* Ensure we don't hit a wall where we have a buffer too small for
3909
     * ap_get_brigade's filters to fetch us another bucket, surrender
3910
     * once we hit 80 bytes (an arbitrary value) less than max_read.
3911
     */
3912
    while (*bytes_read < max_read - 80
3913
           && (APR_BRIGADE_EMPTY(input_brigade)
3914
               || !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade)))) {
3915
        status = ap_get_brigade(r->input_filters, temp_brigade,
3916
                                AP_MODE_READBYTES, block,
3917
                                max_read - *bytes_read);
3918
        /* ap_get_brigade may return success with an empty brigade
3919
         * for a non-blocking read which would block
3920
         */
3921
        if (block == APR_NONBLOCK_READ
3922
                && ((status == APR_SUCCESS && APR_BRIGADE_EMPTY(temp_brigade))
3923
                    || APR_STATUS_IS_EAGAIN(status))) {
3924
            break;
3925
        }
3926
        if (status != APR_SUCCESS) {
3927
            ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01095)
3928
                          "prefetch request body failed to %pI (%s)"
3929
                          " from %s (%s)", backend->addr,
3930
                          backend->hostname ? backend->hostname : "",
3931
                          c->client_ip, c->remote_host ? c->remote_host : "");
3932
            return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
3933
        }
3934
3935
        apr_brigade_length(temp_brigade, 1, &bytes);
3936
        *bytes_read += bytes;
3937
3938
        /*
3939
         * Save temp_brigade in input_brigade. (At least) in the SSL case
3940
         * temp_brigade contains transient buckets whose data would get
3941
         * overwritten during the next call of ap_get_brigade in the loop.
3942
         * ap_save_brigade ensures these buckets to be set aside.
3943
         * Calling ap_save_brigade with NULL as filter is OK, because
3944
         * input_brigade already has been created and does not need to get
3945
         * created by ap_save_brigade.
3946
         */
3947
        status = ap_save_brigade(NULL, &input_brigade, &temp_brigade, p);
3948
        if (status != APR_SUCCESS) {
3949
            ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01096)
3950
                          "processing prefetched request body failed"
3951
                          " to %pI (%s) from %s (%s)", backend->addr,
3952
                          backend->hostname ? backend->hostname : "",
3953
                          c->client_ip, c->remote_host ? c->remote_host : "");
3954
            return HTTP_INTERNAL_SERVER_ERROR;
3955
        }
3956
    }
3957
3958
    return OK;
3959
}
3960
3961
PROXY_DECLARE(int) ap_proxy_read_input(request_rec *r,
3962
                                       proxy_conn_rec *backend,
3963
                                       apr_bucket_brigade *bb,
3964
                                       apr_off_t max_read)
3965
{
3966
    apr_bucket_alloc_t *bucket_alloc = bb->bucket_alloc;
3967
    apr_read_type_e block = (backend->connection) ? APR_NONBLOCK_READ
3968
                                                  : APR_BLOCK_READ;
3969
    apr_status_t status;
3970
    int rv;
3971
3972
    for (;;) {
3973
        apr_brigade_cleanup(bb);
3974
        status = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
3975
                                block, max_read);
3976
        if (block == APR_BLOCK_READ
3977
                || (!(status == APR_SUCCESS && APR_BRIGADE_EMPTY(bb))
3978
                    && !APR_STATUS_IS_EAGAIN(status))) {
3979
            break;
3980
        }
3981
3982
        /* Flush and retry (blocking) */
3983
        apr_brigade_cleanup(bb);
3984
        rv = ap_proxy_pass_brigade(bucket_alloc, r, backend,
3985
                                   backend->connection, bb, 1);
3986
        if (rv != OK) {
3987
            return rv;
3988
        }
3989
        block = APR_BLOCK_READ;
3990
    }
3991
3992
    if (status != APR_SUCCESS) {
3993
        conn_rec *c = r->connection;
3994
        ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02608)
3995
                      "read request body failed to %pI (%s)"
3996
                      " from %s (%s)", backend->addr,
3997
                      backend->hostname ? backend->hostname : "",
3998
                      c->client_ip, c->remote_host ? c->remote_host : "");
3999
        return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
4000
    }
4001
4002
    return OK;
4003
}
4004
4005
PROXY_DECLARE(int) ap_proxy_spool_input(request_rec *r,
4006
                                        proxy_conn_rec *backend,
4007
                                        apr_bucket_brigade *input_brigade,
4008
                                        apr_off_t *bytes_spooled,
4009
                                        apr_off_t max_mem_spool)
4010
{
4011
    apr_pool_t *p = r->pool;
4012
    int seen_eos = 0, rv = OK;
4013
    apr_status_t status = APR_SUCCESS;
4014
    apr_bucket_alloc_t *bucket_alloc = input_brigade->bucket_alloc;
4015
    apr_bucket_brigade *body_brigade;
4016
    apr_bucket *e;
4017
    apr_off_t bytes, fsize = 0;
4018
    apr_file_t *tmpfile = NULL;
4019
    apr_off_t limit;
4020
4021
    *bytes_spooled = 0;
4022
    body_brigade = apr_brigade_create(p, bucket_alloc);
4023
4024
    limit = ap_get_limit_req_body(r);
4025
4026
    do {
4027
        if (APR_BRIGADE_EMPTY(input_brigade)) {
4028
            rv = ap_proxy_read_input(r, backend, input_brigade,
4029
                                     HUGE_STRING_LEN);
4030
            if (rv != OK) {
4031
                return rv;
4032
            }
4033
        }
4034
4035
        /* If this brigade contains EOS, either stop or remove it. */
4036
        if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
4037
            seen_eos = 1;
4038
        }
4039
4040
        apr_brigade_length(input_brigade, 1, &bytes);
4041
4042
        if (*bytes_spooled + bytes > max_mem_spool) {
4043
            /*
4044
             * LimitRequestBody does not affect Proxy requests (Should it?).
4045
             * Let it take effect if we decide to store the body in a
4046
             * temporary file on disk.
4047
             */
4048
            if (limit && (*bytes_spooled + bytes > limit)) {
4049
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01088)
4050
                              "Request body is larger than the configured "
4051
                              "limit of %" APR_OFF_T_FMT, limit);
4052
                return HTTP_REQUEST_ENTITY_TOO_LARGE;
4053
            }
4054
            /* can't spool any more in memory; write latest brigade to disk */
4055
            if (tmpfile == NULL) {
4056
                const char *temp_dir;
4057
                char *template;
4058
4059
                status = apr_temp_dir_get(&temp_dir, p);
4060
                if (status != APR_SUCCESS) {
4061
                    ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01089)
4062
                                  "search for temporary directory failed");
4063
                    return HTTP_INTERNAL_SERVER_ERROR;
4064
                }
4065
                apr_filepath_merge(&template, temp_dir,
4066
                                   "modproxy.tmp.XXXXXX",
4067
                                   APR_FILEPATH_NATIVE, p);
4068
                status = apr_file_mktemp(&tmpfile, template, 0, p);
4069
                if (status != APR_SUCCESS) {
4070
                    ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01090)
4071
                                  "creation of temporary file in directory "
4072
                                  "%s failed", temp_dir);
4073
                    return HTTP_INTERNAL_SERVER_ERROR;
4074
                }
4075
            }
4076
            for (e = APR_BRIGADE_FIRST(input_brigade);
4077
                 e != APR_BRIGADE_SENTINEL(input_brigade);
4078
                 e = APR_BUCKET_NEXT(e)) {
4079
                const char *data;
4080
                apr_size_t bytes_read, bytes_written;
4081
4082
                apr_bucket_read(e, &data, &bytes_read, APR_BLOCK_READ);
4083
                status = apr_file_write_full(tmpfile, data, bytes_read, &bytes_written);
4084
                if (status != APR_SUCCESS) {
4085
                    const char *tmpfile_name;
4086
4087
                    if (apr_file_name_get(&tmpfile_name, tmpfile) != APR_SUCCESS) {
4088
                        tmpfile_name = "(unknown)";
4089
                    }
4090
                    ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01091)
4091
                                  "write to temporary file %s failed",
4092
                                  tmpfile_name);
4093
                    return HTTP_INTERNAL_SERVER_ERROR;
4094
                }
4095
                AP_DEBUG_ASSERT(bytes_read == bytes_written);
4096
                fsize += bytes_written;
4097
            }
4098
            apr_brigade_cleanup(input_brigade);
4099
        }
4100
        else {
4101
4102
            /*
4103
             * Save input_brigade in body_brigade. (At least) in the SSL case
4104
             * input_brigade contains transient buckets whose data would get
4105
             * overwritten during the next call of ap_get_brigade in the loop.
4106
             * ap_save_brigade ensures these buckets to be set aside.
4107
             * Calling ap_save_brigade with NULL as filter is OK, because
4108
             * body_brigade already has been created and does not need to get
4109
             * created by ap_save_brigade.
4110
             */
4111
            status = ap_save_brigade(NULL, &body_brigade, &input_brigade, p);
4112
            if (status != APR_SUCCESS) {
4113
                return HTTP_INTERNAL_SERVER_ERROR;
4114
            }
4115
4116
        }
4117
4118
        *bytes_spooled += bytes;
4119
    } while (!seen_eos);
4120
4121
    APR_BRIGADE_CONCAT(input_brigade, body_brigade);
4122
    if (tmpfile) {
4123
        apr_brigade_insert_file(input_brigade, tmpfile, 0, fsize, p);
4124
    }
4125
    if (apr_table_get(r->subprocess_env, "proxy-sendextracrlf")) {
4126
        e = apr_bucket_immortal_create(CRLF_ASCII, 2, bucket_alloc);
4127
        APR_BRIGADE_INSERT_TAIL(input_brigade, e);
4128
    }
4129
    if (tmpfile) {
4130
        /* We dropped metadata buckets when spooling to tmpfile,
4131
         * terminate with EOS to allow for flushing in a one go.
4132
         */
4133
        e = apr_bucket_eos_create(bucket_alloc);
4134
        APR_BRIGADE_INSERT_TAIL(input_brigade, e);
4135
    }
4136
    return OK;
4137
}
4138
3877
PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc,
4139
PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc,
3878
                                         request_rec *r, proxy_conn_rec *p_conn,
4140
                                         request_rec *r, proxy_conn_rec *p_conn,
3879
                                         conn_rec *origin, apr_bucket_brigade *bb,
4141
                                         conn_rec *origin, apr_bucket_brigade *bb,

Return to bug 57087