1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * @file core_filters.c
19 * @brief Core input/output network filters.
23 #include "apr_strings.h"
25 #include "apr_fnmatch.h"
27 #include "apr_thread_proc.h" /* for RLIMIT stuff */
28 #include "apr_hooks.h"
30 #define APR_WANT_IOVEC
31 #define APR_WANT_STRFUNC
32 #define APR_WANT_MEMFUNC
35 #include "ap_config.h"
37 #include "http_config.h"
38 #include "http_core.h"
39 #include "http_protocol.h" /* For index_of_response(). Grump. */
40 #include "http_request.h"
41 #include "http_vhost.h"
42 #include "http_main.h" /* For the default_handler below... */
45 #include "http_connection.h"
46 #include "apr_buckets.h"
47 #include "util_filter.h"
48 #include "util_ebcdic.h"
49 #include "mpm_common.h"
50 #include "scoreboard.h"
52 #include "mod_proxy.h"
53 #include "ap_listen.h"
55 #include "mod_so.h" /* for ap_find_loaded_module_symbol */
57 #define AP_MIN_SENDFILE_BYTES (256)
60 * Remove all zero length buckets from the brigade.
62 #define BRIGADE_NORMALIZE(b) \
64 apr_bucket *e = APR_BRIGADE_FIRST(b); \
66 if (e->length == 0 && !APR_BUCKET_IS_METADATA(e)) { \
68 d = APR_BUCKET_NEXT(e); \
69 apr_bucket_delete(e); \
73 e = APR_BUCKET_NEXT(e); \
75 } while (!APR_BRIGADE_EMPTY(b) && (e != APR_BRIGADE_SENTINEL(b))); \
80 * Split the contents of a brigade after bucket 'e' to an existing brigade
82 * XXXX: Should this function be added to APR-Util?
84 static void brigade_move(apr_bucket_brigade *b, apr_bucket_brigade *a,
89 if (e != APR_BRIGADE_SENTINEL(b)) {
90 f = APR_RING_LAST(&b->list);
91 APR_RING_UNSPLICE(e, f, link);
92 APR_RING_SPLICE_HEAD(&a->list, e, f, apr_bucket, link);
95 APR_BRIGADE_CHECK_CONSISTENCY(a);
96 APR_BRIGADE_CHECK_CONSISTENCY(b);
99 int ap_core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
100 ap_input_mode_t mode, apr_read_type_e block,
105 core_net_rec *net = f->ctx;
106 core_ctx_t *ctx = net->in_ctx;
110 if (mode == AP_MODE_INIT) {
112 * this mode is for filters that might need to 'initialize'
113 * a connection before reading request data from a client.
114 * NNTP over SSL for example needs to handshake before the
115 * server sends the welcome message.
116 * such filters would have changed the mode before this point
117 * is reached. however, protocol modules such as NNTP should
118 * not need to know anything about SSL. given the example, if
119 * SSL is not in the filter chain, AP_MODE_INIT is a noop.
126 ctx = apr_pcalloc(f->c->pool, sizeof(*ctx));
127 ctx->b = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
128 ctx->tmpbb = apr_brigade_create(ctx->b->p, ctx->b->bucket_alloc);
129 /* seed the brigade with the client socket. */
130 e = apr_bucket_socket_create(net->client_socket, f->c->bucket_alloc);
131 APR_BRIGADE_INSERT_TAIL(ctx->b, e);
134 else if (APR_BRIGADE_EMPTY(ctx->b)) {
138 /* ### This is bad. */
139 BRIGADE_NORMALIZE(ctx->b);
141 /* check for empty brigade again *AFTER* BRIGADE_NORMALIZE()
142 * If we have lost our socket bucket (see above), we are EOF.
144 * Ideally, this should be returning SUCCESS with EOS bucket, but
145 * some higher-up APIs (spec. read_request_line via ap_rgetline)
146 * want an error code. */
147 if (APR_BRIGADE_EMPTY(ctx->b)) {
151 if (mode == AP_MODE_GETLINE) {
152 /* we are reading a single LF line, e.g. the HTTP headers */
153 rv = apr_brigade_split_line(b, ctx->b, block, HUGE_STRING_LEN);
154 /* We should treat EAGAIN here the same as we do for EOF (brigade is
155 * empty). We do this by returning whatever we have read. This may
156 * or may not be bogus, but is consistent (for now) with EOF logic.
158 if (APR_STATUS_IS_EAGAIN(rv)) {
164 /* ### AP_MODE_PEEK is a horrific name for this mode because we also
165 * eat any CRLFs that we see. That's not the obvious intention of
166 * this mode. Determine whether anyone actually uses this or not. */
167 if (mode == AP_MODE_EATCRLF) {
171 /* The purpose of this loop is to ignore any CRLF (or LF) at the end
172 * of a request. Many browsers send extra lines at the end of POST
173 * requests. We use the PEEK method to determine if there is more
174 * data on the socket, so that we know if we should delay sending the
175 * end of one request until we have served the second request in a
176 * pipelined situation. We don't want to actually delay sending a
177 * response if the server finds a CRLF (or LF), becuause that doesn't
178 * mean that there is another request, just a blank line.
181 if (APR_BRIGADE_EMPTY(ctx->b))
184 e = APR_BRIGADE_FIRST(ctx->b);
186 rv = apr_bucket_read(e, &str, &len, APR_NONBLOCK_READ);
188 if (rv != APR_SUCCESS)
192 while (c < str + len) {
193 if (*c == APR_ASCII_LF)
195 else if (*c == APR_ASCII_CR && *(c + 1) == APR_ASCII_LF)
201 /* If we reach here, we were a bucket just full of CRLFs, so
202 * just toss the bucket. */
203 /* FIXME: Is this the right thing to do in the core? */
204 apr_bucket_delete(e);
209 /* If mode is EXHAUSTIVE, we want to just read everything until the end
210 * of the brigade, which in this case means the end of the socket.
211 * To do this, we attach the brigade that has currently been setaside to
212 * the brigade that was passed down, and send that brigade back.
214 * NOTE: This is VERY dangerous to use, and should only be done with
215 * extreme caution. FWLIW, this would be needed by an MPM like Perchild;
216 * such an MPM can easily request the socket and all data that has been
217 * read, which means that it can pass it to the correct child process.
219 if (mode == AP_MODE_EXHAUSTIVE) {
222 /* Tack on any buckets that were set aside. */
223 APR_BRIGADE_CONCAT(b, ctx->b);
225 /* Since we've just added all potential buckets (which will most
226 * likely simply be the socket bucket) we know this is the end,
227 * so tack on an EOS too. */
228 /* We have read until the brigade was empty, so we know that we
230 e = apr_bucket_eos_create(f->c->bucket_alloc);
231 APR_BRIGADE_INSERT_TAIL(b, e);
235 /* read up to the amount they specified. */
236 if (mode == AP_MODE_READBYTES || mode == AP_MODE_SPECULATIVE) {
239 AP_DEBUG_ASSERT(readbytes > 0);
241 e = APR_BRIGADE_FIRST(ctx->b);
242 rv = apr_bucket_read(e, &str, &len, block);
244 if (APR_STATUS_IS_EAGAIN(rv)) {
247 else if (rv != APR_SUCCESS) {
250 else if (block == APR_BLOCK_READ && len == 0) {
251 /* We wanted to read some bytes in blocking mode. We read
252 * 0 bytes. Hence, we now assume we are EOS.
254 * When we are in normal mode, return an EOS bucket to the
256 * When we are in speculative mode, leave ctx->b empty, so
257 * that the next call returns an EOS bucket.
259 apr_bucket_delete(e);
261 if (mode == AP_MODE_READBYTES) {
262 e = apr_bucket_eos_create(f->c->bucket_alloc);
263 APR_BRIGADE_INSERT_TAIL(b, e);
268 /* Have we read as much data as we wanted (be greedy)? */
269 if (len < readbytes) {
270 apr_size_t bucket_len;
273 /* We already registered the data in e in len */
274 e = APR_BUCKET_NEXT(e);
275 while ((len < readbytes) && (rv == APR_SUCCESS)
276 && (e != APR_BRIGADE_SENTINEL(ctx->b))) {
277 /* Check for the availability of buckets with known length */
278 if (e->length != -1) {
280 e = APR_BUCKET_NEXT(e);
284 * Read from bucket, but non blocking. If there isn't any
285 * more data, well than this is fine as well, we will
286 * not wait for more since we already got some and we are
287 * only checking if there isn't more.
289 rv = apr_bucket_read(e, &str, &bucket_len,
291 if (rv == APR_SUCCESS) {
293 e = APR_BUCKET_NEXT(e);
299 /* We can only return at most what we read. */
300 if (len < readbytes) {
304 rv = apr_brigade_partition(ctx->b, readbytes, &e);
305 if (rv != APR_SUCCESS) {
309 /* Must do move before CONCAT */
310 brigade_move(ctx->b, ctx->tmpbb, e);
312 if (mode == AP_MODE_READBYTES) {
313 APR_BRIGADE_CONCAT(b, ctx->b);
315 else if (mode == AP_MODE_SPECULATIVE) {
316 apr_bucket *copy_bucket;
318 for (e = APR_BRIGADE_FIRST(ctx->b);
319 e != APR_BRIGADE_SENTINEL(ctx->b);
320 e = APR_BUCKET_NEXT(e))
322 rv = apr_bucket_copy(e, ©_bucket);
323 if (rv != APR_SUCCESS) {
326 APR_BRIGADE_INSERT_TAIL(b, copy_bucket);
330 /* Take what was originally there and place it back on ctx->b */
331 APR_BRIGADE_CONCAT(ctx->b, ctx->tmpbb);
336 static void setaside_remaining_output(ap_filter_t *f,
337 core_output_filter_ctx_t *ctx,
338 apr_bucket_brigade *bb,
339 int make_a_copy, conn_rec *c);
341 static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
342 apr_bucket_brigade *bb,
343 apr_size_t *bytes_written,
346 static void remove_empty_buckets(apr_bucket_brigade *bb);
348 static apr_status_t send_brigade_blocking(apr_socket_t *s,
349 apr_bucket_brigade *bb,
350 apr_size_t *bytes_written,
353 static apr_status_t writev_nonblocking(apr_socket_t *s,
354 struct iovec *vec, apr_size_t nvec,
355 apr_bucket_brigade *bb,
356 apr_size_t *cumulative_bytes_written,
360 static apr_status_t sendfile_nonblocking(apr_socket_t *s,
362 apr_size_t *cumulative_bytes_written,
366 #define THRESHOLD_MIN_WRITE 4096
367 #define THRESHOLD_MAX_BUFFER 65536
369 /* Optional function coming from mod_logio, used for logging of output
372 extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *ap__logio_add_bytes_out;
374 apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *new_bb)
377 core_net_rec *net = f->ctx;
378 core_output_filter_ctx_t *ctx = net->out_ctx;
379 apr_bucket_brigade *bb;
380 apr_bucket *bucket, *next;
381 apr_size_t bytes_in_brigade, non_file_bytes_in_brigade;
383 /* Fail quickly if the connection has already been aborted. */
385 if (new_bb != NULL) {
386 apr_brigade_cleanup(new_bb);
388 return APR_ECONNABORTED;
393 ctx = apr_pcalloc(c->pool, sizeof(*ctx));
394 net->out_ctx = (core_output_filter_ctx_t *)ctx;
395 rv = apr_socket_opt_set(net->client_socket, APR_SO_NONBLOCK, 1);
396 if (rv != APR_SUCCESS) {
401 if (new_bb != NULL) {
402 for (bucket = APR_BRIGADE_FIRST(new_bb); bucket != APR_BRIGADE_SENTINEL(new_bb); bucket = APR_BUCKET_NEXT(bucket)) {
403 if (bucket->length > 0) {
404 ctx->bytes_in += bucket->length;
409 if ((ctx->buffered_bb != NULL) &&
410 !APR_BRIGADE_EMPTY(ctx->buffered_bb)) {
411 bb = ctx->buffered_bb;
412 ctx->buffered_bb = NULL;
413 if (new_bb != NULL) {
414 APR_BRIGADE_CONCAT(bb, new_bb);
416 c->data_in_output_filters = 0;
418 else if (new_bb != NULL) {
425 /* Scan through the brigade and decide whether to attempt a write,
426 * based on the following rules:
428 * 1) The new_bb is null: Do a nonblocking write of as much as
429 * possible: do a nonblocking write of as much data as possible,
430 * then save the rest in ctx->buffered_bb. (If new_bb == NULL,
431 * it probably means that the MPM is doing asynchronous write
432 * completion and has just determined that this connection
435 * 2) The brigade contains a flush bucket: Do a blocking write
436 * of everything up that point.
438 * 3) The request is in CONN_STATE_HANDLER state, and the brigade
439 * contains at least THRESHOLD_MAX_BUFFER bytes in non-file
440 * buckets: Do blocking writes until the amount of data in the
441 * buffer is less than THRESHOLD_MAX_BUFFER. (The point of this
442 * rule is to provide flow control, in case a handler is
443 * streaming out lots of data faster than the data can be
444 * sent to the client.)
446 * 4) The brigade contains at least THRESHOLD_MIN_WRITE
447 * bytes: Do a nonblocking write of as much data as possible,
448 * then save the rest in ctx->buffered_bb.
451 if (new_bb == NULL) {
452 apr_status_t rv = send_brigade_nonblocking(net->client_socket, bb,
453 &(ctx->bytes_written), c);
454 if (APR_STATUS_IS_EAGAIN(rv)) {
457 else if (rv != APR_SUCCESS) {
458 /* The client has aborted the connection */
461 setaside_remaining_output(f, ctx, bb, 0, c);
465 bytes_in_brigade = 0;
466 non_file_bytes_in_brigade = 0;
467 for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb);
469 next = APR_BUCKET_NEXT(bucket);
470 if (APR_BUCKET_IS_FLUSH(bucket)) {
471 apr_bucket_brigade *remainder = apr_brigade_split(bb, next);
472 apr_status_t rv = send_brigade_blocking(net->client_socket, bb,
473 &(ctx->bytes_written), c);
474 if (rv != APR_SUCCESS) {
475 /* The client has aborted the connection */
480 next = APR_BRIGADE_FIRST(bb);
481 bytes_in_brigade = 0;
482 non_file_bytes_in_brigade = 0;
484 else if (!APR_BUCKET_IS_METADATA(bucket)) {
485 if (bucket->length < 0) {
488 /* XXX support nonblocking read here? */
490 apr_bucket_read(bucket, &data, &length, APR_BLOCK_READ);
491 if (rv != APR_SUCCESS) {
494 /* reading may have split the bucket, so recompute next: */
495 next = APR_BUCKET_NEXT(bucket);
497 bytes_in_brigade += bucket->length;
498 if (!APR_BUCKET_IS_FILE(bucket)) {
499 non_file_bytes_in_brigade += bucket->length;
504 if (non_file_bytes_in_brigade >= THRESHOLD_MAX_BUFFER) {
505 /* ### Writing the entire brigade may be excessive; we really just
506 * ### need to send enough data to be under THRESHOLD_MAX_BUFFER.
508 apr_status_t rv = send_brigade_blocking(net->client_socket, bb,
509 &(ctx->bytes_written), c);
510 if (rv != APR_SUCCESS) {
511 /* The client has aborted the connection */
516 else if (bytes_in_brigade >= THRESHOLD_MIN_WRITE) {
517 apr_status_t rv = send_brigade_nonblocking(net->client_socket, bb,
518 &(ctx->bytes_written), c);
519 if ((rv != APR_SUCCESS) && (!APR_STATUS_IS_EAGAIN(rv))) {
520 /* The client has aborted the connection */
526 setaside_remaining_output(f, ctx, bb, 1, c);
530 static void setaside_remaining_output(ap_filter_t *f,
531 core_output_filter_ctx_t *ctx,
532 apr_bucket_brigade *bb,
533 int make_a_copy, conn_rec *c)
538 remove_empty_buckets(bb);
539 if (!APR_BRIGADE_EMPTY(bb)) {
540 c->data_in_output_filters = 1;
542 /* XXX should this use a separate deferred write pool, like
543 * the original ap_core_output_filter?
545 ap_save_brigade(f, &(ctx->buffered_bb), &bb, c->pool);
546 apr_brigade_destroy(bb);
549 ctx->buffered_bb = bb;
553 apr_brigade_destroy(bb);
557 #ifndef APR_MAX_IOVEC_SIZE
558 #define MAX_IOVEC_TO_WRITE 16
560 #if APR_MAX_IOVEC_SIZE > 16
561 #define MAX_IOVEC_TO_WRITE 16
563 #define MAX_IOVEC_TO_WRITE APR_MAX_IOVEC_SIZE
567 static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
568 apr_bucket_brigade *bb,
569 apr_size_t *bytes_written,
572 apr_bucket *bucket, *next;
574 struct iovec vec[MAX_IOVEC_TO_WRITE];
577 remove_empty_buckets(bb);
579 for (bucket = APR_BRIGADE_FIRST(bb);
580 bucket != APR_BRIGADE_SENTINEL(bb);
582 int did_sendfile = 0;
583 next = APR_BUCKET_NEXT(bucket);
585 if (APR_BUCKET_IS_FILE(bucket)) {
586 apr_bucket_file *file_bucket = (apr_bucket_file *)(bucket->data);
587 apr_file_t *fd = file_bucket->fd;
588 /* Use sendfile to send this file unless:
589 * - the platform doesn't support sendfile,
590 * - the file is too small for sendfile to be useful, or
591 * - sendfile is disabled in the httpd config via "EnableSendfile off"
594 if ((apr_file_flags_get(fd) & APR_SENDFILE_ENABLED) &&
595 (bucket->length >= AP_MIN_SENDFILE_BYTES)) {
598 (void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 1);
599 rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
601 if (rv != APR_SUCCESS) {
602 (void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 0);
606 rv = sendfile_nonblocking(s, bucket, bytes_written, c);
608 (void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 0);
610 if (rv != APR_SUCCESS) {
616 #endif /* APR_HAS_SENDFILE */
617 if (!did_sendfile && !APR_BUCKET_IS_METADATA(bucket)) {
620 rv = apr_bucket_read(bucket, &data, &length, APR_BLOCK_READ);
621 if (rv != APR_SUCCESS) {
624 /* reading may have split the bucket, so recompute next: */
625 next = APR_BUCKET_NEXT(bucket);
626 vec[nvec].iov_base = (char *)data;
627 vec[nvec].iov_len = length;
629 if (nvec == MAX_IOVEC_TO_WRITE) {
630 rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
632 if (rv != APR_SUCCESS) {
641 rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
642 if (rv != APR_SUCCESS) {
647 remove_empty_buckets(bb);
652 static void remove_empty_buckets(apr_bucket_brigade *bb)
655 while (((bucket = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) &&
656 (APR_BUCKET_IS_METADATA(bucket) || (bucket->length == 0))) {
657 APR_BUCKET_REMOVE(bucket);
658 apr_bucket_destroy(bucket);
662 static apr_status_t send_brigade_blocking(apr_socket_t *s,
663 apr_bucket_brigade *bb,
664 apr_size_t *bytes_written,
670 while (!APR_BRIGADE_EMPTY(bb)) {
671 rv = send_brigade_nonblocking(s, bb, bytes_written, c);
672 if (rv != APR_SUCCESS) {
673 if (APR_STATUS_IS_EAGAIN(rv)) {
674 /* Wait until we can send more data */
676 apr_interval_time_t timeout;
677 apr_pollfd_t pollset;
680 pollset.desc_type = APR_POLL_SOCKET;
681 pollset.reqevents = APR_POLLOUT;
683 apr_socket_timeout_get(s, &timeout);
684 rv = apr_poll(&pollset, 1, &nsds, timeout);
685 if (rv != APR_SUCCESS) {
697 static apr_status_t writev_nonblocking(apr_socket_t *s,
698 struct iovec *vec, apr_size_t nvec,
699 apr_bucket_brigade *bb,
700 apr_size_t *cumulative_bytes_written,
703 apr_status_t rv = APR_SUCCESS, arv;
704 apr_size_t bytes_written = 0, bytes_to_write = 0;
705 apr_size_t i, offset;
706 apr_interval_time_t old_timeout;
708 arv = apr_socket_timeout_get(s, &old_timeout);
709 if (arv != APR_SUCCESS) {
712 arv = apr_socket_timeout_set(s, 0);
713 if (arv != APR_SUCCESS) {
717 for (i = 0; i < nvec; i++) {
718 bytes_to_write += vec[i].iov_len;
721 while (bytes_written < bytes_to_write) {
723 rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
726 for (i = offset; i < nvec; ) {
727 apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
728 if (APR_BUCKET_IS_METADATA(bucket)) {
729 APR_BUCKET_REMOVE(bucket);
730 apr_bucket_destroy(bucket);
732 else if (n >= vec[i].iov_len) {
733 APR_BUCKET_REMOVE(bucket);
734 apr_bucket_destroy(bucket);
736 n -= vec[i++].iov_len;
739 apr_bucket_split(bucket, n);
740 APR_BUCKET_REMOVE(bucket);
741 apr_bucket_destroy(bucket);
743 vec[i].iov_base = (char *) vec[i].iov_base + n;
748 if (rv != APR_SUCCESS) {
752 if ((ap__logio_add_bytes_out != NULL) && (bytes_written > 0)) {
753 ap__logio_add_bytes_out(c, bytes_written);
755 *cumulative_bytes_written += bytes_written;
757 arv = apr_socket_timeout_set(s, old_timeout);
758 if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) {
768 static apr_status_t sendfile_nonblocking(apr_socket_t *s,
770 apr_size_t *cumulative_bytes_written,
773 apr_status_t rv = APR_SUCCESS;
774 apr_bucket_file *file_bucket;
776 apr_size_t file_length;
777 apr_off_t file_offset;
778 apr_size_t bytes_written = 0;
780 if (!APR_BUCKET_IS_FILE(bucket)) {
781 ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server,
782 "core_filter: sendfile_nonblocking: "
783 "this should never happen");
786 file_bucket = (apr_bucket_file *)(bucket->data);
787 fd = file_bucket->fd;
788 file_length = bucket->length;
789 file_offset = bucket->start;
791 if (bytes_written < file_length) {
792 apr_size_t n = file_length - bytes_written;
794 apr_interval_time_t old_timeout;
796 arv = apr_socket_timeout_get(s, &old_timeout);
797 if (arv != APR_SUCCESS) {
800 arv = apr_socket_timeout_set(s, 0);
801 if (arv != APR_SUCCESS) {
804 rv = apr_socket_sendfile(s, fd, NULL, &file_offset, &n, 0);
805 if (rv == APR_SUCCESS) {
809 arv = apr_socket_timeout_set(s, old_timeout);
810 if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) {
814 if ((ap__logio_add_bytes_out != NULL) && (bytes_written > 0)) {
815 ap__logio_add_bytes_out(c, bytes_written);
817 *cumulative_bytes_written += bytes_written;
818 if ((bytes_written < file_length) && (bytes_written > 0)) {
819 apr_bucket_split(bucket, bytes_written);
820 APR_BUCKET_REMOVE(bucket);
821 apr_bucket_destroy(bucket);
823 else if (bytes_written == file_length) {
824 APR_BUCKET_REMOVE(bucket);
825 apr_bucket_destroy(bucket);