summaryrefslogtreecommitdiffstats
path: root/buckets
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:30:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:30:05 +0000
commita1e354165254cd9e346751e6c2ddc554feeb0e6d (patch)
tree5fd273cc604fd00efd630eb387a6f79ce102f4e3 /buckets
parentInitial commit. (diff)
downloadapr-util-a1e354165254cd9e346751e6c2ddc554feeb0e6d.tar.xz
apr-util-a1e354165254cd9e346751e6c2ddc554feeb0e6d.zip
Adding upstream version 1.6.3.upstream/1.6.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'buckets')
-rw-r--r--buckets/apr_brigade.c736
-rw-r--r--buckets/apr_buckets.c46
-rw-r--r--buckets/apr_buckets_alloc.c243
-rw-r--r--buckets/apr_buckets_eos.c54
-rw-r--r--buckets/apr_buckets_file.c264
-rw-r--r--buckets/apr_buckets_flush.c54
-rw-r--r--buckets/apr_buckets_heap.c96
-rw-r--r--buckets/apr_buckets_mmap.c144
-rw-r--r--buckets/apr_buckets_pipe.c119
-rw-r--r--buckets/apr_buckets_pool.c142
-rw-r--r--buckets/apr_buckets_refcount.c64
-rw-r--r--buckets/apr_buckets_simple.c137
-rw-r--r--buckets/apr_buckets_socket.c114
13 files changed, 2213 insertions, 0 deletions
diff --git a/buckets/apr_brigade.c b/buckets/apr_brigade.c
new file mode 100644
index 0000000..1f2ba17
--- /dev/null
+++ b/buckets/apr_brigade.c
@@ -0,0 +1,736 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_pools.h"
+#include "apr_tables.h"
+#include "apr_buckets.h"
+#include "apr_errno.h"
+#define APR_WANT_MEMFUNC
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+#if APR_HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+static apr_status_t brigade_cleanup(void *data)
+{
+ return apr_brigade_cleanup(data);
+}
+
+APU_DECLARE(apr_status_t) apr_brigade_cleanup(void *data)
+{
+ apr_bucket_brigade *b = data;
+ apr_bucket *e;
+
+ while (!APR_BRIGADE_EMPTY(b)) {
+ e = APR_BRIGADE_FIRST(b);
+ apr_bucket_delete(e);
+ }
+ /* We don't need to free(bb) because it's allocated from a pool. */
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(apr_status_t) apr_brigade_destroy(apr_bucket_brigade *b)
+{
+ apr_pool_cleanup_kill(b->p, b, brigade_cleanup);
+ return apr_brigade_cleanup(b);
+}
+
+APU_DECLARE(apr_bucket_brigade *) apr_brigade_create(apr_pool_t *p,
+ apr_bucket_alloc_t *list)
+{
+ apr_bucket_brigade *b;
+
+ b = apr_palloc(p, sizeof(*b));
+ b->p = p;
+ b->bucket_alloc = list;
+
+ APR_RING_INIT(&b->list, apr_bucket, link);
+
+ apr_pool_cleanup_register(b->p, b, brigade_cleanup, apr_pool_cleanup_null);
+ return b;
+}
+
+APU_DECLARE(apr_bucket_brigade *) apr_brigade_split_ex(apr_bucket_brigade *b,
+ apr_bucket *e,
+ apr_bucket_brigade *a)
+{
+ apr_bucket *f;
+
+ if (!a) {
+ a = apr_brigade_create(b->p, b->bucket_alloc);
+ }
+ else if (!APR_BRIGADE_EMPTY(a)) {
+ apr_brigade_cleanup(a);
+ }
+ /* Return an empty brigade if there is nothing left in
+ * the first brigade to split off
+ */
+ if (e != APR_BRIGADE_SENTINEL(b)) {
+ f = APR_RING_LAST(&b->list);
+ APR_RING_UNSPLICE(e, f, link);
+ APR_RING_SPLICE_HEAD(&a->list, e, f, apr_bucket, link);
+ }
+
+ APR_BRIGADE_CHECK_CONSISTENCY(a);
+ APR_BRIGADE_CHECK_CONSISTENCY(b);
+
+ return a;
+}
+
+APU_DECLARE(apr_bucket_brigade *) apr_brigade_split(apr_bucket_brigade *b,
+ apr_bucket *e)
+{
+ return apr_brigade_split_ex(b, e, NULL);
+}
+
+APU_DECLARE(apr_status_t) apr_brigade_partition(apr_bucket_brigade *b,
+ apr_off_t point,
+ apr_bucket **after_point)
+{
+ apr_bucket *e;
+ const char *s;
+ apr_size_t len;
+ apr_uint64_t point64;
+ apr_status_t rv;
+
+ if (point < 0) {
+ /* this could cause weird (not necessarily SEGV) things to happen */
+ return APR_EINVAL;
+ }
+ if (point == 0) {
+ *after_point = APR_BRIGADE_FIRST(b);
+ return APR_SUCCESS;
+ }
+
+ /*
+ * Try to reduce the following casting mess: We know that point will be
+ * larger equal 0 now and forever and thus that point (apr_off_t) and
+ * apr_size_t will fit into apr_uint64_t in any case.
+ */
+ point64 = (apr_uint64_t)point;
+
+ APR_BRIGADE_CHECK_CONSISTENCY(b);
+
+ for (e = APR_BRIGADE_FIRST(b);
+ e != APR_BRIGADE_SENTINEL(b);
+ e = APR_BUCKET_NEXT(e))
+ {
+ /* For an unknown length bucket, while 'point64' is beyond the possible
+ * size contained in apr_size_t, read and continue...
+ */
+ if ((e->length == (apr_size_t)(-1))
+ && (point64 > (apr_uint64_t)APR_SIZE_MAX)) {
+ /* point64 is too far out to simply split this bucket,
+ * we must fix this bucket's size and keep going... */
+ rv = apr_bucket_read(e, &s, &len, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ *after_point = e;
+ return rv;
+ }
+ }
+ else if ((point64 < (apr_uint64_t)e->length)
+ || (e->length == (apr_size_t)(-1))) {
+ /* We already consumed buckets where point64 is beyond
+ * our interest ( point64 > APR_SIZE_MAX ), above.
+ * Here point falls between 0 and APR_SIZE_MAX
+ * and is within this bucket, or this bucket's len
+ * is undefined, so now we are ready to split it.
+ * First try to split the bucket natively... */
+ if ((rv = apr_bucket_split(e, (apr_size_t)point64))
+ != APR_ENOTIMPL) {
+ *after_point = APR_BUCKET_NEXT(e);
+ return rv;
+ }
+
+ /* if the bucket cannot be split, we must read from it,
+ * changing its type to one that can be split */
+ rv = apr_bucket_read(e, &s, &len, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ *after_point = e;
+ return rv;
+ }
+
+ /* this assumes that len == e->length, which is okay because e
+ * might have been morphed by the apr_bucket_read() above, but
+ * if it was, the length would have been adjusted appropriately */
+ if (point64 < (apr_uint64_t)e->length) {
+ rv = apr_bucket_split(e, (apr_size_t)point64);
+ *after_point = APR_BUCKET_NEXT(e);
+ return rv;
+ }
+ }
+ if (point64 == (apr_uint64_t)e->length) {
+ *after_point = APR_BUCKET_NEXT(e);
+ return APR_SUCCESS;
+ }
+ point64 -= (apr_uint64_t)e->length;
+ }
+ *after_point = APR_BRIGADE_SENTINEL(b);
+ return APR_INCOMPLETE;
+}
+
+APU_DECLARE(apr_status_t) apr_brigade_length(apr_bucket_brigade *bb,
+ int read_all, apr_off_t *length)
+{
+ apr_off_t total = 0;
+ apr_bucket *bkt;
+ apr_status_t status = APR_SUCCESS;
+
+ for (bkt = APR_BRIGADE_FIRST(bb);
+ bkt != APR_BRIGADE_SENTINEL(bb);
+ bkt = APR_BUCKET_NEXT(bkt))
+ {
+ if (bkt->length == (apr_size_t)(-1)) {
+ const char *ignore;
+ apr_size_t len;
+
+ if (!read_all) {
+ total = -1;
+ break;
+ }
+
+ if ((status = apr_bucket_read(bkt, &ignore, &len,
+ APR_BLOCK_READ)) != APR_SUCCESS) {
+ break;
+ }
+ }
+
+ total += bkt->length;
+ }
+
+ *length = total;
+ return status;
+}
+
+APU_DECLARE(apr_status_t) apr_brigade_flatten(apr_bucket_brigade *bb,
+ char *c, apr_size_t *len)
+{
+ apr_size_t actual = 0;
+ apr_bucket *b;
+
+ for (b = APR_BRIGADE_FIRST(bb);
+ b != APR_BRIGADE_SENTINEL(bb);
+ b = APR_BUCKET_NEXT(b))
+ {
+ const char *str;
+ apr_size_t str_len;
+ apr_status_t status;
+
+ status = apr_bucket_read(b, &str, &str_len, APR_BLOCK_READ);
+ if (status != APR_SUCCESS) {
+ return status;
+ }
+
+ /* If we would overflow. */
+ if (str_len + actual > *len) {
+ str_len = *len - actual;
+ }
+
+ /* XXX: It appears that overflow of the final bucket
+ * is DISCARDED without any warning to the caller.
+ *
+ * No, we only copy the data up to their requested size. -- jre
+ */
+ memcpy(c, str, str_len);
+
+ c += str_len;
+ actual += str_len;
+
+ /* This could probably be actual == *len, but be safe from stray
+ * photons. */
+ if (actual >= *len) {
+ break;
+ }
+ }
+
+ *len = actual;
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(apr_status_t) apr_brigade_pflatten(apr_bucket_brigade *bb,
+ char **c,
+ apr_size_t *len,
+ apr_pool_t *pool)
+{
+ apr_off_t actual;
+ apr_size_t total;
+ apr_status_t rv;
+
+ apr_brigade_length(bb, 1, &actual);
+
+ /* XXX: This is dangerous beyond belief. At least in the
+ * apr_brigade_flatten case, the user explicitly stated their
+ * buffer length - so we don't up and palloc 4GB for a single
+ * file bucket. This API must grow a useful max boundry,
+ * either compiled-in or preset via the *len value.
+ *
+ * Shouldn't both fn's grow an additional return value for
+ * the case that the brigade couldn't be flattened into the
+ * provided or allocated buffer (such as APR_EMOREDATA?)
+ * Not a failure, simply an advisory result.
+ */
+ total = (apr_size_t)actual;
+
+ *c = apr_palloc(pool, total);
+
+ rv = apr_brigade_flatten(bb, *c, &total);
+
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ *len = total;
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(apr_status_t) apr_brigade_split_line(apr_bucket_brigade *bbOut,
+ apr_bucket_brigade *bbIn,
+ apr_read_type_e block,
+ apr_off_t maxbytes)
+{
+ apr_off_t readbytes = 0;
+
+ while (!APR_BRIGADE_EMPTY(bbIn)) {
+ const char *pos;
+ const char *str;
+ apr_size_t len;
+ apr_status_t rv;
+ apr_bucket *e;
+
+ e = APR_BRIGADE_FIRST(bbIn);
+ rv = apr_bucket_read(e, &str, &len, block);
+
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ pos = memchr(str, APR_ASCII_LF, len);
+ /* We found a match. */
+ if (pos != NULL) {
+ apr_bucket_split(e, pos - str + 1);
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(bbOut, e);
+ return APR_SUCCESS;
+ }
+ APR_BUCKET_REMOVE(e);
+ if (APR_BUCKET_IS_METADATA(e) || len > APR_BUCKET_BUFF_SIZE/4) {
+ APR_BRIGADE_INSERT_TAIL(bbOut, e);
+ }
+ else {
+ if (len > 0) {
+ rv = apr_brigade_write(bbOut, NULL, NULL, str, len);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+ apr_bucket_destroy(e);
+ }
+ readbytes += len;
+ /* We didn't find an APR_ASCII_LF within the maximum line length. */
+ if (readbytes >= maxbytes) {
+ break;
+ }
+ }
+
+ return APR_SUCCESS;
+}
+
+
+APU_DECLARE(apr_status_t) apr_brigade_to_iovec(apr_bucket_brigade *b,
+ struct iovec *vec, int *nvec)
+{
+ int left = *nvec;
+ apr_bucket *e;
+ struct iovec *orig;
+ apr_size_t iov_len;
+ const char *iov_base;
+ apr_status_t rv;
+
+ orig = vec;
+
+ for (e = APR_BRIGADE_FIRST(b);
+ e != APR_BRIGADE_SENTINEL(b);
+ e = APR_BUCKET_NEXT(e))
+ {
+ if (left-- == 0)
+ break;
+
+ rv = apr_bucket_read(e, &iov_base, &iov_len, APR_NONBLOCK_READ);
+ if (rv != APR_SUCCESS)
+ return rv;
+ /* Set indirectly since types differ: */
+ vec->iov_len = iov_len;
+ vec->iov_base = (void *)iov_base;
+ ++vec;
+ }
+
+ *nvec = (int)(vec - orig);
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(apr_status_t) apr_brigade_vputstrs(apr_bucket_brigade *b,
+ apr_brigade_flush flush,
+ void *ctx,
+ va_list va)
+{
+#define MAX_VECS 8
+ struct iovec vec[MAX_VECS];
+ apr_size_t i = 0;
+
+ for (;;) {
+ char *str = va_arg(va, char *);
+ apr_status_t rv;
+
+ if (str == NULL)
+ break;
+
+ vec[i].iov_base = str;
+ vec[i].iov_len = strlen(str);
+ i++;
+
+ if (i == MAX_VECS) {
+ rv = apr_brigade_writev(b, flush, ctx, vec, i);
+ if (rv != APR_SUCCESS)
+ return rv;
+ i = 0;
+ }
+ }
+ if (i != 0)
+ return apr_brigade_writev(b, flush, ctx, vec, i);
+
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(apr_status_t) apr_brigade_putc(apr_bucket_brigade *b,
+ apr_brigade_flush flush, void *ctx,
+ const char c)
+{
+ return apr_brigade_write(b, flush, ctx, &c, 1);
+}
+
+APU_DECLARE(apr_status_t) apr_brigade_write(apr_bucket_brigade *b,
+ apr_brigade_flush flush,
+ void *ctx,
+ const char *str, apr_size_t nbyte)
+{
+ apr_bucket *e = APR_BRIGADE_LAST(b);
+ apr_size_t remaining = APR_BUCKET_BUFF_SIZE;
+ char *buf = NULL;
+
+ /*
+ * If the last bucket is a heap bucket and its buffer is not shared with
+ * another bucket, we may write into that bucket.
+ */
+ if (!APR_BRIGADE_EMPTY(b) && APR_BUCKET_IS_HEAP(e)
+ && ((apr_bucket_heap *)(e->data))->refcount.refcount == 1) {
+ apr_bucket_heap *h = e->data;
+
+ /* HEAP bucket start offsets are always in-memory, safe to cast */
+ remaining = h->alloc_len - (e->length + (apr_size_t)e->start);
+ buf = h->base + e->start + e->length;
+ }
+
+ if (nbyte > remaining) {
+ /* either a buffer bucket exists but is full,
+ * or no buffer bucket exists and the data is too big
+ * to buffer. In either case, we should flush. */
+ if (flush) {
+ e = apr_bucket_transient_create(str, nbyte, b->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ return flush(b, ctx);
+ }
+ else {
+ e = apr_bucket_heap_create(str, nbyte, NULL, b->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ return APR_SUCCESS;
+ }
+ }
+ else if (!buf) {
+ /* we don't have a buffer, but the data is small enough
+ * that we don't mind making a new buffer */
+ buf = apr_bucket_alloc(APR_BUCKET_BUFF_SIZE, b->bucket_alloc);
+ e = apr_bucket_heap_create(buf, APR_BUCKET_BUFF_SIZE,
+ apr_bucket_free, b->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ e->length = 0; /* We are writing into the brigade, and
+ * allocating more memory than we need. This
+ * ensures that the bucket thinks it is empty just
+ * after we create it. We'll fix the length
+ * once we put data in it below.
+ */
+ }
+
+ /* there is a sufficiently big buffer bucket available now */
+ memcpy(buf, str, nbyte);
+ e->length += nbyte;
+
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(apr_status_t) apr_brigade_writev(apr_bucket_brigade *b,
+ apr_brigade_flush flush,
+ void *ctx,
+ const struct iovec *vec,
+ apr_size_t nvec)
+{
+ apr_bucket *e;
+ apr_size_t total_len;
+ apr_size_t i;
+ char *buf;
+
+ /* Compute the total length of the data to be written.
+ */
+ total_len = 0;
+ for (i = 0; i < nvec; i++) {
+ total_len += vec[i].iov_len;
+ }
+
+ /* If the data to be written is very large, try to convert
+ * the iovec to transient buckets rather than copying.
+ */
+ if (total_len > APR_BUCKET_BUFF_SIZE) {
+ if (flush) {
+ for (i = 0; i < nvec; i++) {
+ e = apr_bucket_transient_create(vec[i].iov_base,
+ vec[i].iov_len,
+ b->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ }
+ return flush(b, ctx);
+ }
+ else {
+ for (i = 0; i < nvec; i++) {
+ e = apr_bucket_heap_create((const char *) vec[i].iov_base,
+ vec[i].iov_len, NULL,
+ b->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ }
+ return APR_SUCCESS;
+ }
+ }
+
+ i = 0;
+
+ /* If there is a heap bucket at the end of the brigade
+ * already, and its refcount is 1, copy into the existing bucket.
+ */
+ e = APR_BRIGADE_LAST(b);
+ if (!APR_BRIGADE_EMPTY(b) && APR_BUCKET_IS_HEAP(e)
+ && ((apr_bucket_heap *)(e->data))->refcount.refcount == 1) {
+ apr_bucket_heap *h = e->data;
+ apr_size_t remaining = h->alloc_len -
+ (e->length + (apr_size_t)e->start);
+ buf = h->base + e->start + e->length;
+
+ if (remaining >= total_len) {
+ /* Simple case: all the data will fit in the
+ * existing heap bucket
+ */
+ for (; i < nvec; i++) {
+ apr_size_t len = vec[i].iov_len;
+ memcpy(buf, (const void *) vec[i].iov_base, len);
+ buf += len;
+ }
+ e->length += total_len;
+ return APR_SUCCESS;
+ }
+ else {
+ /* More complicated case: not all of the data
+ * will fit in the existing heap bucket. The
+ * total data size is <= APR_BUCKET_BUFF_SIZE,
+ * so we'll need only one additional bucket.
+ */
+ const char *start_buf = buf;
+ for (; i < nvec; i++) {
+ apr_size_t len = vec[i].iov_len;
+ if (len > remaining) {
+ break;
+ }
+ memcpy(buf, (const void *) vec[i].iov_base, len);
+ buf += len;
+ remaining -= len;
+ }
+ e->length += (buf - start_buf);
+ total_len -= (buf - start_buf);
+
+ if (flush) {
+ apr_status_t rv = flush(b, ctx);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+
+ /* Now fall through into the case below to
+ * allocate another heap bucket and copy the
+ * rest of the array. (Note that i is not
+ * reset to zero here; it holds the index
+ * of the first vector element to be
+ * written to the new bucket.)
+ */
+ }
+ }
+
+ /* Allocate a new heap bucket, and copy the data into it.
+ * The checks above ensure that the amount of data to be
+ * written here is no larger than APR_BUCKET_BUFF_SIZE.
+ */
+ buf = apr_bucket_alloc(APR_BUCKET_BUFF_SIZE, b->bucket_alloc);
+ e = apr_bucket_heap_create(buf, APR_BUCKET_BUFF_SIZE,
+ apr_bucket_free, b->bucket_alloc);
+ for (; i < nvec; i++) {
+ apr_size_t len = vec[i].iov_len;
+ memcpy(buf, (const void *) vec[i].iov_base, len);
+ buf += len;
+ }
+ e->length = total_len;
+ APR_BRIGADE_INSERT_TAIL(b, e);
+
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(apr_status_t) apr_brigade_puts(apr_bucket_brigade *bb,
+ apr_brigade_flush flush, void *ctx,
+ const char *str)
+{
+ return apr_brigade_write(bb, flush, ctx, str, strlen(str));
+}
+
+APU_DECLARE_NONSTD(apr_status_t) apr_brigade_putstrs(apr_bucket_brigade *b,
+ apr_brigade_flush flush,
+ void *ctx, ...)
+{
+ va_list va;
+ apr_status_t rv;
+
+ va_start(va, ctx);
+ rv = apr_brigade_vputstrs(b, flush, ctx, va);
+ va_end(va);
+ return rv;
+}
+
+APU_DECLARE_NONSTD(apr_status_t) apr_brigade_printf(apr_bucket_brigade *b,
+ apr_brigade_flush flush,
+ void *ctx,
+ const char *fmt, ...)
+{
+ va_list ap;
+ apr_status_t rv;
+
+ va_start(ap, fmt);
+ rv = apr_brigade_vprintf(b, flush, ctx, fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+struct brigade_vprintf_data_t {
+ apr_vformatter_buff_t vbuff;
+
+ apr_bucket_brigade *b; /* associated brigade */
+ apr_brigade_flush *flusher; /* flushing function */
+ void *ctx;
+
+ char *cbuff; /* buffer to flush from */
+};
+
+static apr_status_t brigade_flush(apr_vformatter_buff_t *buff)
+{
+ /* callback function passed to ap_vformatter to be
+ * called when vformatter needs to buff and
+ * buff.curpos > buff.endpos
+ */
+
+ /* "downcast," have really passed a brigade_vprintf_data_t* */
+ struct brigade_vprintf_data_t *vd = (struct brigade_vprintf_data_t*)buff;
+ apr_status_t res = APR_SUCCESS;
+
+ res = apr_brigade_write(vd->b, *vd->flusher, vd->ctx, vd->cbuff,
+ APR_BUCKET_BUFF_SIZE);
+
+ if(res != APR_SUCCESS) {
+ return -1;
+ }
+
+ vd->vbuff.curpos = vd->cbuff;
+ vd->vbuff.endpos = vd->cbuff + APR_BUCKET_BUFF_SIZE;
+
+ return res;
+}
+
+APU_DECLARE(apr_status_t) apr_brigade_vprintf(apr_bucket_brigade *b,
+ apr_brigade_flush flush,
+ void *ctx,
+ const char *fmt, va_list va)
+{
+ /* the cast, in order of appearance */
+ struct brigade_vprintf_data_t vd;
+ char buf[APR_BUCKET_BUFF_SIZE];
+ int written;
+
+ vd.vbuff.curpos = buf;
+ vd.vbuff.endpos = buf + APR_BUCKET_BUFF_SIZE;
+ vd.b = b;
+ vd.flusher = &flush;
+ vd.ctx = ctx;
+ vd.cbuff = buf;
+
+ written = apr_vformatter(brigade_flush, &vd.vbuff, fmt, va);
+
+ if (written == -1) {
+ return -1;
+ }
+
+ /* write out what remains in the buffer */
+ return apr_brigade_write(b, flush, ctx, buf, vd.vbuff.curpos - buf);
+}
+
+/* A "safe" maximum bucket size, 1Gb */
+#define MAX_BUCKET_SIZE (0x40000000)
+
+APU_DECLARE(apr_bucket *) apr_brigade_insert_file(apr_bucket_brigade *bb,
+ apr_file_t *f,
+ apr_off_t start,
+ apr_off_t length,
+ apr_pool_t *p)
+{
+ apr_bucket *e;
+
+ if (sizeof(apr_off_t) == sizeof(apr_size_t) || length < MAX_BUCKET_SIZE) {
+ e = apr_bucket_file_create(f, start, (apr_size_t)length, p,
+ bb->bucket_alloc);
+ }
+ else {
+ /* Several buckets are needed. */
+ e = apr_bucket_file_create(f, start, MAX_BUCKET_SIZE, p,
+ bb->bucket_alloc);
+
+ while (length > MAX_BUCKET_SIZE) {
+ apr_bucket *ce;
+ apr_bucket_copy(e, &ce);
+ APR_BRIGADE_INSERT_TAIL(bb, ce);
+ e->start += MAX_BUCKET_SIZE;
+ length -= MAX_BUCKET_SIZE;
+ }
+ e->length = (apr_size_t)length; /* Resize just the last bucket */
+ }
+
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ return e;
+}
diff --git a/buckets/apr_buckets.c b/buckets/apr_buckets.c
new file mode 100644
index 0000000..802f4e2
--- /dev/null
+++ b/buckets/apr_buckets.c
@@ -0,0 +1,46 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_buckets.h"
+
+APU_DECLARE_NONSTD(apr_status_t) apr_bucket_setaside_noop(apr_bucket *data,
+ apr_pool_t *pool)
+{
+ return APR_SUCCESS;
+}
+
+APU_DECLARE_NONSTD(apr_status_t) apr_bucket_setaside_notimpl(apr_bucket *data,
+ apr_pool_t *pool)
+{
+ return APR_ENOTIMPL;
+}
+
+APU_DECLARE_NONSTD(apr_status_t) apr_bucket_split_notimpl(apr_bucket *data,
+ apr_size_t point)
+{
+ return APR_ENOTIMPL;
+}
+
+APU_DECLARE_NONSTD(apr_status_t) apr_bucket_copy_notimpl(apr_bucket *e,
+ apr_bucket **c)
+{
+ return APR_ENOTIMPL;
+}
+
+APU_DECLARE_NONSTD(void) apr_bucket_destroy_noop(void *data)
+{
+ return;
+}
diff --git a/buckets/apr_buckets_alloc.c b/buckets/apr_buckets_alloc.c
new file mode 100644
index 0000000..2d6f214
--- /dev/null
+++ b/buckets/apr_buckets_alloc.c
@@ -0,0 +1,243 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include "apr_buckets.h"
+#include "apr_allocator.h"
+#include "apr_version.h"
+
+#define ALLOC_AMT (8192 - APR_MEMNODE_T_SIZE)
+
+typedef struct node_header_t {
+ apr_size_t size;
+ apr_bucket_alloc_t *alloc;
+ apr_memnode_t *memnode;
+ struct node_header_t *next;
+} node_header_t;
+
+#define SIZEOF_NODE_HEADER_T APR_ALIGN_DEFAULT(sizeof(node_header_t))
+#define SMALL_NODE_SIZE (APR_BUCKET_ALLOC_SIZE + SIZEOF_NODE_HEADER_T)
+
+/** A list of free memory from which new buckets or private bucket
+ * structures can be allocated.
+ */
+struct apr_bucket_alloc_t {
+ apr_pool_t *pool;
+ apr_allocator_t *allocator;
+ node_header_t *freelist;
+ apr_memnode_t *blocks;
+};
+
+static apr_status_t alloc_cleanup(void *data)
+{
+ apr_bucket_alloc_t *list = data;
+#if APR_POOL_DEBUG
+ apr_allocator_t *allocator = NULL;
+#endif
+
+#if APR_POOL_DEBUG
+ if (list->pool && list->allocator != apr_pool_allocator_get(list->pool)) {
+ allocator = list->allocator;
+ }
+#endif
+
+ apr_allocator_free(list->allocator, list->blocks);
+
+#if APR_POOL_DEBUG
+ if (allocator) {
+ apr_allocator_destroy(allocator);
+ }
+#endif
+
+ return APR_SUCCESS;
+}
+
+APU_DECLARE_NONSTD(apr_bucket_alloc_t *) apr_bucket_alloc_create(apr_pool_t *p)
+{
+ apr_allocator_t *allocator = apr_pool_allocator_get(p);
+ apr_bucket_alloc_t *list;
+
+#if APR_POOL_DEBUG
+ /* may be NULL for debug mode. */
+ if (allocator == NULL) {
+ if (apr_allocator_create(&allocator) != APR_SUCCESS) {
+ apr_abortfunc_t fn = apr_pool_abort_get(p);
+ if (fn)
+ (fn)(APR_ENOMEM);
+ abort();
+ }
+ }
+#endif
+ list = apr_bucket_alloc_create_ex(allocator);
+ if (list == NULL) {
+ apr_abortfunc_t fn = apr_pool_abort_get(p);
+ if (fn)
+ (fn)(APR_ENOMEM);
+ abort();
+ }
+ list->pool = p;
+ apr_pool_cleanup_register(list->pool, list, alloc_cleanup,
+ apr_pool_cleanup_null);
+
+ return list;
+}
+
+APU_DECLARE_NONSTD(apr_bucket_alloc_t *) apr_bucket_alloc_create_ex(
+ apr_allocator_t *allocator)
+{
+ apr_bucket_alloc_t *list;
+ apr_memnode_t *block;
+
+ block = apr_allocator_alloc(allocator, ALLOC_AMT);
+ if (!block) {
+ return NULL;
+ }
+ list = (apr_bucket_alloc_t *)block->first_avail;
+ list->pool = NULL;
+ list->allocator = allocator;
+ list->freelist = NULL;
+ list->blocks = block;
+ block->first_avail += APR_ALIGN_DEFAULT(sizeof(*list));
+
+ return list;
+}
+
+APU_DECLARE_NONSTD(void) apr_bucket_alloc_destroy(apr_bucket_alloc_t *list)
+{
+ if (list->pool) {
+ apr_pool_cleanup_kill(list->pool, list, alloc_cleanup);
+ }
+
+ apr_allocator_free(list->allocator, list->blocks);
+
+#if APR_POOL_DEBUG
+ if (list->pool && list->allocator != apr_pool_allocator_get(list->pool)) {
+ apr_allocator_destroy(list->allocator);
+ }
+#endif
+}
+
+APU_DECLARE_NONSTD(apr_size_t) apr_bucket_alloc_aligned_floor(apr_bucket_alloc_t *list,
+ apr_size_t size)
+{
+ if (size <= SMALL_NODE_SIZE) {
+ size = SMALL_NODE_SIZE;
+ }
+ else {
+#if APR_VERSION_AT_LEAST(1,6,0)
+ if (size < APR_MEMNODE_T_SIZE) {
+ size = apr_allocator_align(list->allocator, 0);
+ }
+ else {
+ size = apr_allocator_align(list->allocator,
+ size - APR_MEMNODE_T_SIZE);
+ }
+#else
+ /* Assumes the minimum (default) allocator's boundary of 4K and
+ * minimum (immutable before APR-1.6.x) allocation size of 8K,
+ * hence possibly (yet unlikely) under-estimating the floor...
+ */
+ size = APR_ALIGN(size, 4096);
+ if (size < 8192) {
+ size = 8192;
+ }
+#endif
+ size -= APR_MEMNODE_T_SIZE;
+ }
+ size -= SIZEOF_NODE_HEADER_T;
+ return size;
+}
+
+APU_DECLARE_NONSTD(void *) apr_bucket_alloc(apr_size_t size,
+ apr_bucket_alloc_t *list)
+{
+ node_header_t *node;
+ apr_memnode_t *active = list->blocks;
+ char *endp;
+
+ size += SIZEOF_NODE_HEADER_T;
+ if (size <= SMALL_NODE_SIZE) {
+ if (list->freelist) {
+ node = list->freelist;
+ list->freelist = node->next;
+ }
+ else {
+ endp = active->first_avail + SMALL_NODE_SIZE;
+ if (endp >= active->endp) {
+ list->blocks = apr_allocator_alloc(list->allocator, ALLOC_AMT);
+ if (!list->blocks) {
+ list->blocks = active;
+ return NULL;
+ }
+ list->blocks->next = active;
+ active = list->blocks;
+ endp = active->first_avail + SMALL_NODE_SIZE;
+ }
+ node = (node_header_t *)active->first_avail;
+ node->alloc = list;
+ node->memnode = active;
+ node->size = SMALL_NODE_SIZE;
+ active->first_avail = endp;
+ }
+ }
+ else {
+ apr_memnode_t *memnode = apr_allocator_alloc(list->allocator, size);
+ if (!memnode) {
+ return NULL;
+ }
+ node = (node_header_t *)memnode->first_avail;
+ node->alloc = list;
+ node->memnode = memnode;
+ node->size = size;
+ }
+ return ((char *)node) + SIZEOF_NODE_HEADER_T;
+}
+
+#ifdef APR_BUCKET_DEBUG
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+static void check_not_already_free(node_header_t *node)
+{
+ apr_bucket_alloc_t *list = node->alloc;
+ node_header_t *curr = list->freelist;
+
+ while (curr) {
+ if (node == curr) {
+ abort();
+ }
+ curr = curr->next;
+ }
+}
+#else
+#define check_not_already_free(node)
+#endif
+
+APU_DECLARE_NONSTD(void) apr_bucket_free(void *mem)
+{
+ node_header_t *node = (node_header_t *)((char *)mem - SIZEOF_NODE_HEADER_T);
+ apr_bucket_alloc_t *list = node->alloc;
+
+ if (node->size == SMALL_NODE_SIZE) {
+ check_not_already_free(node);
+ node->next = list->freelist;
+ list->freelist = node;
+ }
+ else {
+ apr_allocator_free(list->allocator, node->memnode);
+ }
+}
diff --git a/buckets/apr_buckets_eos.c b/buckets/apr_buckets_eos.c
new file mode 100644
index 0000000..25cff75
--- /dev/null
+++ b/buckets/apr_buckets_eos.c
@@ -0,0 +1,54 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_buckets.h"
+
+static apr_status_t eos_bucket_read(apr_bucket *b, const char **str,
+ apr_size_t *len, apr_read_type_e block)
+{
+ *str = NULL;
+ *len = 0;
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_eos_make(apr_bucket *b)
+{
+ b->length = 0;
+ b->start = 0;
+ b->data = NULL;
+ b->type = &apr_bucket_type_eos;
+
+ return b;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_eos_create(apr_bucket_alloc_t *list)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ return apr_bucket_eos_make(b);
+}
+
+APU_DECLARE_DATA const apr_bucket_type_t apr_bucket_type_eos = {
+ "EOS", 5, APR_BUCKET_METADATA,
+ apr_bucket_destroy_noop,
+ eos_bucket_read,
+ apr_bucket_setaside_noop,
+ apr_bucket_split_notimpl,
+ apr_bucket_simple_copy
+};
diff --git a/buckets/apr_buckets_file.c b/buckets/apr_buckets_file.c
new file mode 100644
index 0000000..a6f03cc
--- /dev/null
+++ b/buckets/apr_buckets_file.c
@@ -0,0 +1,264 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr.h"
+#include "apr_general.h"
+#include "apr_file_io.h"
+#include "apr_buckets.h"
+
+#if APR_HAS_MMAP
+#include "apr_mmap.h"
+
+/* mmap support for static files based on ideas from John Heidemann's
+ * patch against 1.0.5. See
+ * <http://www.isi.edu/~johnh/SOFTWARE/APACHE/index.html>.
+ */
+
+#endif /* APR_HAS_MMAP */
+
+static void file_bucket_destroy(void *data)
+{
+ apr_bucket_file *f = data;
+
+ if (apr_bucket_shared_destroy(f)) {
+ /* no need to close the file here; it will get
+ * done automatically when the pool gets cleaned up */
+ apr_bucket_free(f);
+ }
+}
+
+#if APR_HAS_MMAP
+static int file_make_mmap(apr_bucket *e, apr_size_t filelength,
+ apr_off_t fileoffset, apr_pool_t *p)
+{
+ apr_bucket_file *a = e->data;
+ apr_mmap_t *mm;
+
+ if (!a->can_mmap) {
+ return 0;
+ }
+
+ if (filelength > APR_MMAP_LIMIT) {
+ if (apr_mmap_create(&mm, a->fd, fileoffset, APR_MMAP_LIMIT,
+ APR_MMAP_READ, p) != APR_SUCCESS)
+ {
+ return 0;
+ }
+ apr_bucket_split(e, APR_MMAP_LIMIT);
+ filelength = APR_MMAP_LIMIT;
+ }
+ else if ((filelength < APR_MMAP_THRESHOLD) ||
+ (apr_mmap_create(&mm, a->fd, fileoffset, filelength,
+ APR_MMAP_READ, p) != APR_SUCCESS))
+ {
+ return 0;
+ }
+ apr_bucket_mmap_make(e, mm, 0, filelength);
+ file_bucket_destroy(a);
+ return 1;
+}
+#endif
+
+static apr_status_t file_bucket_read(apr_bucket *e, const char **str,
+ apr_size_t *len, apr_read_type_e block)
+{
+ apr_bucket_file *a = e->data;
+ apr_file_t *f = a->fd;
+ apr_bucket *b = NULL;
+ char *buf;
+ apr_status_t rv;
+ apr_size_t filelength = e->length; /* bytes remaining in file past offset */
+ apr_off_t fileoffset = e->start;
+#if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES
+ apr_int32_t flags;
+#endif
+
+#if APR_HAS_MMAP
+ if (file_make_mmap(e, filelength, fileoffset, a->readpool)) {
+ return apr_bucket_read(e, str, len, block);
+ }
+#endif
+
+#if APR_HAS_THREADS && !APR_HAS_XTHREAD_FILES
+ if ((flags = apr_file_flags_get(f)) & APR_FOPEN_XTHREAD) {
+ /* this file descriptor is shared across multiple threads and
+ * this OS doesn't support that natively, so as a workaround
+ * we must reopen the file into a->readpool */
+ const char *fname;
+ apr_file_name_get(&fname, f);
+
+ rv = apr_file_open(&f, fname, (flags & ~APR_FOPEN_XTHREAD), 0, a->readpool);
+ if (rv != APR_SUCCESS)
+ return rv;
+
+ a->fd = f;
+ }
+#endif
+
+ *str = NULL; /* in case we die prematurely */
+ *len = (filelength > a->read_size) ? a->read_size : filelength;
+ buf = apr_bucket_alloc(*len, e->list);
+
+ /* Handle offset ... */
+ rv = apr_file_seek(f, APR_SET, &fileoffset);
+ if (rv != APR_SUCCESS) {
+ apr_bucket_free(buf);
+ return rv;
+ }
+ rv = apr_file_read(f, buf, len);
+ if (rv != APR_SUCCESS && rv != APR_EOF) {
+ apr_bucket_free(buf);
+ return rv;
+ }
+ filelength -= *len;
+ /*
+ * Change the current bucket to refer to what we read,
+ * even if we read nothing because we hit EOF.
+ */
+ apr_bucket_heap_make(e, buf, *len, apr_bucket_free);
+
+ /* If we have more to read from the file, then create another bucket */
+ if (filelength > 0 && rv != APR_EOF) {
+ /* for efficiency, we can just build a new apr_bucket struct
+ * to wrap around the existing file bucket */
+ b = apr_bucket_alloc(sizeof(*b), e->list);
+ b->start = fileoffset + (*len);
+ b->length = filelength;
+ b->data = a;
+ b->type = &apr_bucket_type_file;
+ b->free = apr_bucket_free;
+ b->list = e->list;
+ APR_BUCKET_INSERT_AFTER(e, b);
+ }
+ else {
+ file_bucket_destroy(a);
+ }
+
+ *str = buf;
+ return rv;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_file_make(apr_bucket *b, apr_file_t *fd,
+ apr_off_t offset,
+ apr_size_t len, apr_pool_t *p)
+{
+ apr_bucket_file *f;
+
+ f = apr_bucket_alloc(sizeof(*f), b->list);
+ f->fd = fd;
+ f->readpool = p;
+#if APR_HAS_MMAP
+ f->can_mmap = 1;
+#endif
+ f->read_size = APR_BUCKET_BUFF_SIZE;
+
+ b = apr_bucket_shared_make(b, f, offset, len);
+ b->type = &apr_bucket_type_file;
+
+ return b;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_file_create(apr_file_t *fd,
+ apr_off_t offset,
+ apr_size_t len, apr_pool_t *p,
+ apr_bucket_alloc_t *list)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ return apr_bucket_file_make(b, fd, offset, len, p);
+}
+
+APU_DECLARE(apr_status_t) apr_bucket_file_enable_mmap(apr_bucket *e,
+ int enabled)
+{
+#if APR_HAS_MMAP
+ apr_bucket_file *a = e->data;
+ a->can_mmap = enabled;
+ return APR_SUCCESS;
+#else
+ return APR_ENOTIMPL;
+#endif /* APR_HAS_MMAP */
+}
+
+APU_DECLARE(apr_status_t) apr_bucket_file_set_buf_size(apr_bucket *e,
+ apr_size_t size)
+{
+ apr_bucket_file *a = e->data;
+
+ if (size <= APR_BUCKET_BUFF_SIZE) {
+ a->read_size = APR_BUCKET_BUFF_SIZE;
+ }
+ else {
+ apr_size_t floor = apr_bucket_alloc_aligned_floor(e->list, size);
+ a->read_size = (size < floor) ? size : floor;
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t file_bucket_setaside(apr_bucket *b, apr_pool_t *reqpool)
+{
+ apr_bucket_file *a = b->data;
+ apr_file_t *fd = NULL;
+ apr_file_t *f = a->fd;
+ apr_pool_t *curpool = apr_file_pool_get(f);
+
+ if (apr_pool_is_ancestor(curpool, reqpool)) {
+ return APR_SUCCESS;
+ }
+
+ /* If the file is shared/split accross multiple buckets, this bucket can't
+ * take exclusive ownership with apr_file_setaside() (thus invalidating the
+ * f->filedes), let's apr_file_dup() in this case instead.
+ */
+ if (a->refcount.refcount > 1) {
+ apr_bucket_file *new;
+ apr_status_t rv;
+
+ rv = apr_file_dup(&fd, f, reqpool);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ new = apr_bucket_alloc(sizeof(*new), b->list);
+ memcpy(new, a, sizeof(*new));
+ new->refcount.refcount = 1;
+ new->readpool = reqpool;
+
+ a->refcount.refcount--;
+ a = b->data = new;
+ }
+ else {
+ apr_file_setaside(&fd, f, reqpool);
+ if (!apr_pool_is_ancestor(a->readpool, reqpool)) {
+ a->readpool = reqpool;
+ }
+ }
+ a->fd = fd;
+ return APR_SUCCESS;
+}
+
+APU_DECLARE_DATA const apr_bucket_type_t apr_bucket_type_file = {
+ "FILE", 5, APR_BUCKET_DATA,
+ file_bucket_destroy,
+ file_bucket_read,
+ file_bucket_setaside,
+ apr_bucket_shared_split,
+ apr_bucket_shared_copy
+};
diff --git a/buckets/apr_buckets_flush.c b/buckets/apr_buckets_flush.c
new file mode 100644
index 0000000..a5d84d7
--- /dev/null
+++ b/buckets/apr_buckets_flush.c
@@ -0,0 +1,54 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_buckets.h"
+
+static apr_status_t flush_bucket_read(apr_bucket *b, const char **str,
+ apr_size_t *len, apr_read_type_e block)
+{
+ *str = NULL;
+ *len = 0;
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_flush_make(apr_bucket *b)
+{
+ b->length = 0;
+ b->start = 0;
+ b->data = NULL;
+ b->type = &apr_bucket_type_flush;
+
+ return b;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_flush_create(apr_bucket_alloc_t *list)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ return apr_bucket_flush_make(b);
+}
+
+APU_DECLARE_DATA const apr_bucket_type_t apr_bucket_type_flush = {
+ "FLUSH", 5, APR_BUCKET_METADATA,
+ apr_bucket_destroy_noop,
+ flush_bucket_read,
+ apr_bucket_setaside_noop,
+ apr_bucket_split_notimpl,
+ apr_bucket_simple_copy
+};
diff --git a/buckets/apr_buckets_heap.c b/buckets/apr_buckets_heap.c
new file mode 100644
index 0000000..00f9808
--- /dev/null
+++ b/buckets/apr_buckets_heap.c
@@ -0,0 +1,96 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_buckets.h"
+#define APR_WANT_MEMFUNC
+#include "apr_want.h"
+
+static apr_status_t heap_bucket_read(apr_bucket *b, const char **str,
+ apr_size_t *len, apr_read_type_e block)
+{
+ apr_bucket_heap *h = b->data;
+
+ *str = h->base + b->start;
+ *len = b->length;
+ return APR_SUCCESS;
+}
+
+static void heap_bucket_destroy(void *data)
+{
+ apr_bucket_heap *h = data;
+
+ if (apr_bucket_shared_destroy(h)) {
+ (*h->free_func)(h->base);
+ apr_bucket_free(h);
+ }
+}
+
+/* Warning: if you change this function, be sure to
+ * change apr_bucket_pool_make() too! */
+APU_DECLARE(apr_bucket *) apr_bucket_heap_make(apr_bucket *b, const char *buf,
+ apr_size_t length,
+ void (*free_func)(void *data))
+{
+ apr_bucket_heap *h;
+
+ h = apr_bucket_alloc(sizeof(*h), b->list);
+
+ if (!free_func) {
+ h->alloc_len = length;
+ h->base = apr_bucket_alloc(h->alloc_len, b->list);
+ if (h->base == NULL) {
+ apr_bucket_free(h);
+ return NULL;
+ }
+ h->free_func = apr_bucket_free;
+ memcpy(h->base, buf, length);
+ }
+ else {
+ /* XXX: we lose the const qualifier here which indicates
+ * there's something screwy with the API...
+ */
+ h->base = (char *) buf;
+ h->alloc_len = length;
+ h->free_func = free_func;
+ }
+
+ b = apr_bucket_shared_make(b, h, 0, length);
+ b->type = &apr_bucket_type_heap;
+
+ return b;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_heap_create(const char *buf,
+ apr_size_t length,
+ void (*free_func)(void *data),
+ apr_bucket_alloc_t *list)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ return apr_bucket_heap_make(b, buf, length, free_func);
+}
+
+APU_DECLARE_DATA const apr_bucket_type_t apr_bucket_type_heap = {
+ "HEAP", 5, APR_BUCKET_DATA,
+ heap_bucket_destroy,
+ heap_bucket_read,
+ apr_bucket_setaside_noop,
+ apr_bucket_shared_split,
+ apr_bucket_shared_copy
+};
diff --git a/buckets/apr_buckets_mmap.c b/buckets/apr_buckets_mmap.c
new file mode 100644
index 0000000..19de291
--- /dev/null
+++ b/buckets/apr_buckets_mmap.c
@@ -0,0 +1,144 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_buckets.h"
+
+#if APR_HAS_MMAP
+
+static apr_status_t mmap_bucket_read(apr_bucket *b, const char **str,
+ apr_size_t *length, apr_read_type_e block)
+{
+ apr_bucket_mmap *m = b->data;
+ apr_status_t ok;
+ void *addr;
+
+ if (!m->mmap) {
+ /* the apr_mmap_t was already cleaned up out from under us */
+ return APR_EINVAL;
+ }
+
+ ok = apr_mmap_offset(&addr, m->mmap, b->start);
+ if (ok != APR_SUCCESS) {
+ return ok;
+ }
+ *str = addr;
+ *length = b->length;
+ return APR_SUCCESS;
+}
+
+static apr_status_t mmap_bucket_cleanup(void *data)
+{
+ /* the apr_mmap_t is about to disappear out from under us, so we
+ * have no choice but to pretend it doesn't exist anymore. the
+ * refcount is now useless because there's nothing to refer to
+ * anymore. so the only valid action on any remaining referrer
+ * is to delete it. no more reads, no more anything. */
+ apr_bucket_mmap *m = data;
+
+ m->mmap = NULL;
+ return APR_SUCCESS;
+}
+
+static void mmap_bucket_destroy(void *data)
+{
+ apr_bucket_mmap *m = data;
+
+ if (apr_bucket_shared_destroy(m)) {
+ if (m->mmap) {
+ apr_pool_cleanup_kill(m->mmap->cntxt, m, mmap_bucket_cleanup);
+ apr_mmap_delete(m->mmap);
+ }
+ apr_bucket_free(m);
+ }
+}
+
+/*
+ * XXX: are the start and length arguments useful?
+ */
+APU_DECLARE(apr_bucket *) apr_bucket_mmap_make(apr_bucket *b, apr_mmap_t *mm,
+ apr_off_t start,
+ apr_size_t length)
+{
+ apr_bucket_mmap *m;
+
+ m = apr_bucket_alloc(sizeof(*m), b->list);
+ m->mmap = mm;
+
+ apr_pool_cleanup_register(mm->cntxt, m, mmap_bucket_cleanup,
+ apr_pool_cleanup_null);
+
+ b = apr_bucket_shared_make(b, m, start, length);
+ b->type = &apr_bucket_type_mmap;
+
+ return b;
+}
+
+
+APU_DECLARE(apr_bucket *) apr_bucket_mmap_create(apr_mmap_t *mm,
+ apr_off_t start,
+ apr_size_t length,
+ apr_bucket_alloc_t *list)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ return apr_bucket_mmap_make(b, mm, start, length);
+}
+
+static apr_status_t mmap_bucket_setaside(apr_bucket *b, apr_pool_t *p)
+{
+ apr_bucket_mmap *m = b->data;
+ apr_mmap_t *mm = m->mmap;
+ apr_mmap_t *new_mm;
+ apr_status_t ok;
+
+ if (!mm) {
+ /* the apr_mmap_t was already cleaned up out from under us */
+ return APR_EINVAL;
+ }
+
+ /* shortcut if possible */
+ if (apr_pool_is_ancestor(mm->cntxt, p)) {
+ return APR_SUCCESS;
+ }
+
+ /* duplicate apr_mmap_t into new pool */
+ ok = apr_mmap_dup(&new_mm, mm, p);
+ if (ok != APR_SUCCESS) {
+ return ok;
+ }
+
+ /* decrement refcount on old apr_bucket_mmap */
+ mmap_bucket_destroy(m);
+
+ /* create new apr_bucket_mmap pointing to new apr_mmap_t */
+ apr_bucket_mmap_make(b, new_mm, b->start, b->length);
+
+ return APR_SUCCESS;
+}
+
+APU_DECLARE_DATA const apr_bucket_type_t apr_bucket_type_mmap = {
+ "MMAP", 5, APR_BUCKET_DATA,
+ mmap_bucket_destroy,
+ mmap_bucket_read,
+ mmap_bucket_setaside,
+ apr_bucket_shared_split,
+ apr_bucket_shared_copy
+};
+
+#endif
diff --git a/buckets/apr_buckets_pipe.c b/buckets/apr_buckets_pipe.c
new file mode 100644
index 0000000..46b4697
--- /dev/null
+++ b/buckets/apr_buckets_pipe.c
@@ -0,0 +1,119 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_buckets.h"
+
+static apr_status_t pipe_bucket_read(apr_bucket *a, const char **str,
+ apr_size_t *len, apr_read_type_e block)
+{
+ apr_file_t *p = a->data;
+ char *buf;
+ apr_status_t rv;
+ apr_interval_time_t timeout;
+
+ if (block == APR_NONBLOCK_READ) {
+ apr_file_pipe_timeout_get(p, &timeout);
+ apr_file_pipe_timeout_set(p, 0);
+ }
+
+ *str = NULL;
+ *len = APR_BUCKET_BUFF_SIZE;
+ buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */
+
+ rv = apr_file_read(p, buf, len);
+
+ if (block == APR_NONBLOCK_READ) {
+ apr_file_pipe_timeout_set(p, timeout);
+ }
+
+ if (rv != APR_SUCCESS && rv != APR_EOF) {
+ apr_bucket_free(buf);
+ return rv;
+ }
+ /*
+ * If there's more to read we have to keep the rest of the pipe
+ * for later. Otherwise, we'll close the pipe.
+ * XXX: Note that more complicated bucket types that
+ * refer to data not in memory and must therefore have a read()
+ * function similar to this one should be wary of copying this
+ * code because if they have a destroy function they probably
+ * want to migrate the bucket's subordinate structure from the
+ * old bucket to a raw new one and adjust it as appropriate,
+ * rather than destroying the old one and creating a completely
+ * new bucket.
+ */
+ if (*len > 0) {
+ apr_bucket_heap *h;
+ /* Change the current bucket to refer to what we read */
+ a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free);
+ h = a->data;
+ h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */
+ *str = buf;
+ APR_BUCKET_INSERT_AFTER(a, apr_bucket_pipe_create(p, a->list));
+ }
+ else {
+ apr_bucket_free(buf);
+ a = apr_bucket_immortal_make(a, "", 0);
+ *str = a->data;
+ if (rv == APR_EOF) {
+ apr_file_close(p);
+ }
+ }
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_pipe_make(apr_bucket *b, apr_file_t *p)
+{
+ /*
+ * A pipe is closed when the end is reached in pipe_bucket_read(). If
+ * the pipe isn't read to the end (e.g., error path), the pipe will be
+ * closed when its pool goes away.
+ *
+ * Note that typically the pipe is allocated from the request pool
+ * so it will disappear when the request is finished. However the
+ * core filter may decide to set aside the tail end of a CGI
+ * response if the connection is pipelined. This turns out not to
+ * be a problem because the core will have read to the end of the
+ * stream so the bucket(s) that it sets aside will be the heap
+ * buckets created by pipe_bucket_read() above.
+ */
+ b->type = &apr_bucket_type_pipe;
+ b->length = (apr_size_t)(-1);
+ b->start = -1;
+ b->data = p;
+
+ return b;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_pipe_create(apr_file_t *p,
+ apr_bucket_alloc_t *list)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ return apr_bucket_pipe_make(b, p);
+}
+
+APU_DECLARE_DATA const apr_bucket_type_t apr_bucket_type_pipe = {
+ "PIPE", 5, APR_BUCKET_DATA,
+ apr_bucket_destroy_noop,
+ pipe_bucket_read,
+ apr_bucket_setaside_notimpl,
+ apr_bucket_split_notimpl,
+ apr_bucket_copy_notimpl
+};
diff --git a/buckets/apr_buckets_pool.c b/buckets/apr_buckets_pool.c
new file mode 100644
index 0000000..56ba585
--- /dev/null
+++ b/buckets/apr_buckets_pool.c
@@ -0,0 +1,142 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_buckets.h"
+#define APR_WANT_MEMFUNC
+#include "apr_want.h"
+
+static apr_status_t pool_bucket_cleanup(void *data)
+{
+ apr_bucket_pool *p = data;
+
+ /*
+ * If the pool gets cleaned up, we have to copy the data out
+ * of the pool and onto the heap. But the apr_buckets out there
+ * that point to this pool bucket need to be notified such that
+ * they can morph themselves into a regular heap bucket the next
+ * time they try to read. To avoid having to manipulate
+ * reference counts and b->data pointers, the apr_bucket_pool
+ * actually _contains_ an apr_bucket_heap as its first element,
+ * so the two share their apr_bucket_refcount member, and you
+ * can typecast a pool bucket struct to make it look like a
+ * regular old heap bucket struct.
+ */
+ p->heap.base = apr_bucket_alloc(p->heap.alloc_len, p->list);
+ memcpy(p->heap.base, p->base, p->heap.alloc_len);
+ p->base = NULL;
+ p->pool = NULL;
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t pool_bucket_read(apr_bucket *b, const char **str,
+ apr_size_t *len, apr_read_type_e block)
+{
+ apr_bucket_pool *p = b->data;
+ const char *base = p->base;
+
+ if (p->pool == NULL) {
+ /*
+ * pool has been cleaned up... masquerade as a heap bucket from now
+ * on. subsequent bucket operations will use the heap bucket code.
+ */
+ b->type = &apr_bucket_type_heap;
+ base = p->heap.base;
+ }
+ *str = base + b->start;
+ *len = b->length;
+ return APR_SUCCESS;
+}
+
+static void pool_bucket_destroy(void *data)
+{
+ apr_bucket_pool *p = data;
+
+ /* If the pool is cleaned up before the last reference goes
+ * away, the data is really now on the heap; heap_destroy() takes
+ * over. free() in heap_destroy() thinks it's freeing
+ * an apr_bucket_heap, when in reality it's freeing the whole
+ * apr_bucket_pool for us.
+ */
+ if (p->pool) {
+ /* the shared resource is still in the pool
+ * because the pool has not been cleaned up yet
+ */
+ if (apr_bucket_shared_destroy(p)) {
+ apr_pool_cleanup_kill(p->pool, p, pool_bucket_cleanup);
+ apr_bucket_free(p);
+ }
+ }
+ else {
+ /* the shared resource is no longer in the pool, it's
+ * on the heap, but this reference still thinks it's a pool
+ * bucket. we should just go ahead and pass control to
+ * heap_destroy() for it since it doesn't know any better.
+ */
+ apr_bucket_type_heap.destroy(p);
+ }
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_pool_make(apr_bucket *b,
+ const char *buf, apr_size_t length, apr_pool_t *pool)
+{
+ apr_bucket_pool *p;
+
+ p = apr_bucket_alloc(sizeof(*p), b->list);
+
+ /* XXX: we lose the const qualifier here which indicates
+ * there's something screwy with the API...
+ */
+ /* XXX: why is this? buf is const, p->base is const... what's
+ * the problem? --jcw */
+ p->base = (char *) buf;
+ p->pool = pool;
+ p->list = b->list;
+
+ b = apr_bucket_shared_make(b, p, 0, length);
+ b->type = &apr_bucket_type_pool;
+
+ /* pre-initialize heap bucket member */
+ p->heap.alloc_len = length;
+ p->heap.base = NULL;
+ p->heap.free_func = apr_bucket_free;
+
+ apr_pool_cleanup_register(p->pool, p, pool_bucket_cleanup,
+ apr_pool_cleanup_null);
+ return b;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_pool_create(const char *buf,
+ apr_size_t length,
+ apr_pool_t *pool,
+ apr_bucket_alloc_t *list)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ return apr_bucket_pool_make(b, buf, length, pool);
+}
+
+APU_DECLARE_DATA const apr_bucket_type_t apr_bucket_type_pool = {
+ "POOL", 5, APR_BUCKET_DATA,
+ pool_bucket_destroy,
+ pool_bucket_read,
+ apr_bucket_setaside_noop, /* don't need to setaside thanks to the cleanup*/
+ apr_bucket_shared_split,
+ apr_bucket_shared_copy
+};
diff --git a/buckets/apr_buckets_refcount.c b/buckets/apr_buckets_refcount.c
new file mode 100644
index 0000000..0e765d9
--- /dev/null
+++ b/buckets/apr_buckets_refcount.c
@@ -0,0 +1,64 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_buckets.h"
+
+APU_DECLARE_NONSTD(apr_status_t) apr_bucket_shared_split(apr_bucket *a,
+ apr_size_t point)
+{
+ apr_bucket_refcount *r = a->data;
+ apr_status_t rv;
+
+ if ((rv = apr_bucket_simple_split(a, point)) != APR_SUCCESS) {
+ return rv;
+ }
+ r->refcount++;
+
+ return APR_SUCCESS;
+}
+
+APU_DECLARE_NONSTD(apr_status_t) apr_bucket_shared_copy(apr_bucket *a,
+ apr_bucket **b)
+{
+ apr_bucket_refcount *r = a->data;
+
+ apr_bucket_simple_copy(a, b);
+ r->refcount++;
+
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(int) apr_bucket_shared_destroy(void *data)
+{
+ apr_bucket_refcount *r = data;
+ r->refcount--;
+ return (r->refcount == 0);
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_shared_make(apr_bucket *b, void *data,
+ apr_off_t start,
+ apr_size_t length)
+{
+ apr_bucket_refcount *r = data;
+
+ b->data = r;
+ b->start = start;
+ b->length = length;
+ /* caller initializes the type field */
+ r->refcount = 1;
+
+ return b;
+}
diff --git a/buckets/apr_buckets_simple.c b/buckets/apr_buckets_simple.c
new file mode 100644
index 0000000..cef748b
--- /dev/null
+++ b/buckets/apr_buckets_simple.c
@@ -0,0 +1,137 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_buckets.h"
+
+APU_DECLARE_NONSTD(apr_status_t) apr_bucket_simple_copy(apr_bucket *a,
+ apr_bucket **b)
+{
+ *b = apr_bucket_alloc(sizeof(**b), a->list); /* XXX: check for failure? */
+ **b = *a;
+
+ return APR_SUCCESS;
+}
+
+APU_DECLARE_NONSTD(apr_status_t) apr_bucket_simple_split(apr_bucket *a,
+ apr_size_t point)
+{
+ apr_bucket *b;
+
+ if (point > a->length) {
+ return APR_EINVAL;
+ }
+
+ apr_bucket_simple_copy(a, &b);
+
+ a->length = point;
+ b->length -= point;
+ b->start += point;
+
+ APR_BUCKET_INSERT_AFTER(a, b);
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t simple_bucket_read(apr_bucket *b, const char **str,
+ apr_size_t *len, apr_read_type_e block)
+{
+ *str = (char *)b->data + b->start;
+ *len = b->length;
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_immortal_make(apr_bucket *b,
+ const char *buf,
+ apr_size_t length)
+{
+ b->data = (char *)buf;
+ b->length = length;
+ b->start = 0;
+ b->type = &apr_bucket_type_immortal;
+
+ return b;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_immortal_create(const char *buf,
+ apr_size_t length,
+ apr_bucket_alloc_t *list)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ return apr_bucket_immortal_make(b, buf, length);
+}
+
+/*
+ * XXX: This function could do with some tweaking to reduce memory
+ * usage in various cases, e.g. share buffers in the heap between all
+ * the buckets that are set aside, or even spool set-aside data to
+ * disk if it gets too voluminous (but if it does then that's probably
+ * a bug elsewhere). There should probably be a apr_brigade_setaside()
+ * function that co-ordinates the action of all the bucket setaside
+ * functions to improve memory efficiency.
+ */
+static apr_status_t transient_bucket_setaside(apr_bucket *b, apr_pool_t *pool)
+{
+ b = apr_bucket_heap_make(b, (char *)b->data + b->start, b->length, NULL);
+ if (b == NULL) {
+ return APR_ENOMEM;
+ }
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_transient_make(apr_bucket *b,
+ const char *buf,
+ apr_size_t length)
+{
+ b->data = (char *)buf;
+ b->length = length;
+ b->start = 0;
+ b->type = &apr_bucket_type_transient;
+ return b;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_transient_create(const char *buf,
+ apr_size_t length,
+ apr_bucket_alloc_t *list)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ return apr_bucket_transient_make(b, buf, length);
+}
+
+const apr_bucket_type_t apr_bucket_type_immortal = {
+ "IMMORTAL", 5, APR_BUCKET_DATA,
+ apr_bucket_destroy_noop,
+ simple_bucket_read,
+ apr_bucket_setaside_noop,
+ apr_bucket_simple_split,
+ apr_bucket_simple_copy
+};
+
+APU_DECLARE_DATA const apr_bucket_type_t apr_bucket_type_transient = {
+ "TRANSIENT", 5, APR_BUCKET_DATA,
+ apr_bucket_destroy_noop,
+ simple_bucket_read,
+ transient_bucket_setaside,
+ apr_bucket_simple_split,
+ apr_bucket_simple_copy
+};
diff --git a/buckets/apr_buckets_socket.c b/buckets/apr_buckets_socket.c
new file mode 100644
index 0000000..68eae43
--- /dev/null
+++ b/buckets/apr_buckets_socket.c
@@ -0,0 +1,114 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_buckets.h"
+
+static apr_status_t socket_bucket_read(apr_bucket *a, const char **str,
+ apr_size_t *len, apr_read_type_e block)
+{
+ apr_socket_t *p = a->data;
+ char *buf;
+ apr_status_t rv;
+ apr_interval_time_t timeout;
+
+ if (block == APR_NONBLOCK_READ) {
+ apr_socket_timeout_get(p, &timeout);
+ apr_socket_timeout_set(p, 0);
+ }
+
+ *str = NULL;
+ *len = APR_BUCKET_BUFF_SIZE;
+ buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */
+
+ rv = apr_socket_recv(p, buf, len);
+
+ if (block == APR_NONBLOCK_READ) {
+ apr_socket_timeout_set(p, timeout);
+ }
+
+ if (rv != APR_SUCCESS && rv != APR_EOF) {
+ apr_bucket_free(buf);
+ return rv;
+ }
+ /*
+ * If there's more to read we have to keep the rest of the socket
+ * for later. XXX: Note that more complicated bucket types that
+ * refer to data not in memory and must therefore have a read()
+ * function similar to this one should be wary of copying this
+ * code because if they have a destroy function they probably
+ * want to migrate the bucket's subordinate structure from the
+ * old bucket to a raw new one and adjust it as appropriate,
+ * rather than destroying the old one and creating a completely
+ * new bucket.
+ *
+ * Even if there is nothing more to read, don't close the socket here
+ * as we have to use it to send any response :) We could shut it
+ * down for reading, but there is no benefit to doing so.
+ */
+ if (*len > 0) {
+ apr_bucket_heap *h;
+ /* Change the current bucket to refer to what we read */
+ a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free);
+ h = a->data;
+ h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */
+ *str = buf;
+ APR_BUCKET_INSERT_AFTER(a, apr_bucket_socket_create(p, a->list));
+ }
+ else {
+ apr_bucket_free(buf);
+ a = apr_bucket_immortal_make(a, "", 0);
+ *str = a->data;
+ }
+ return APR_SUCCESS;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_socket_make(apr_bucket *b, apr_socket_t *p)
+{
+ /*
+ * XXX: We rely on a cleanup on some pool or other to actually
+ * destroy the socket. We should probably explicitly call apr to
+ * destroy it instead.
+ *
+ * Note that typically the socket is allocated from the connection pool
+ * so it will disappear when the connection is finished.
+ */
+ b->type = &apr_bucket_type_socket;
+ b->length = (apr_size_t)(-1);
+ b->start = -1;
+ b->data = p;
+
+ return b;
+}
+
+APU_DECLARE(apr_bucket *) apr_bucket_socket_create(apr_socket_t *p,
+ apr_bucket_alloc_t *list)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ return apr_bucket_socket_make(b, p);
+}
+
+APU_DECLARE_DATA const apr_bucket_type_t apr_bucket_type_socket = {
+ "SOCKET", 5, APR_BUCKET_DATA,
+ apr_bucket_destroy_noop,
+ socket_bucket_read,
+ apr_bucket_setaside_notimpl,
+ apr_bucket_split_notimpl,
+ apr_bucket_copy_notimpl
+};