The bucket interface is documented in srclib/apr-util/include/apr_buckets.h.
Buckets come in various flavors — currently
there are file, pipe, and socket
buckets. There are buckets that are simply data in memory, but even
these have various types — transient, heap, pool, memory-mapped,
and immortal. There are also special EOS (end of stream) and flush
buckets. Even though all buckets provide a way to read the bucket
data (or as much as is currently available) via
apr_bucket_read()
— which is actually more
like a peek interface — it is still necessary to consume the data
somehow, either by destroying the bucket, reducing it in size, or
splitting it. The read can be chosen to be either blocking or
nonblocking — in either case, if data is available, it will all
be returned.
Note that because the data is not destroyed by the read operation, it may be necessary for the bucket to change type and/or add extra buckets to the brigade — for example, consider a socket bucket: when you read it, it will read whatever is currently available from the socket and replace itself with a memory bucket containing that data. It will also add a new socket bucket following the memory bucket. (It can’t simply insert the memory bucket before the socket bucket — that way, you’d have no way to find the pointer to the memory bucket, or even know it had been created.) So, although the current bucket pointer remains valid, it may change type as a result of a read, and the contents of the brigade may also change.
Although one cannot destructively read from a brigade, one can write
to one — there are lots of functions to do that, ranging from
apr_brigade_putc()
to
apr_brigade_printf()
.
EOS buckets indicate the end of the current stream (e.g., the end of a request), and flush buckets indicate that the filter should flush any stored data (assuming it can, of course). It is vital to obey such instructions (and pass them on), as failure will often cause deadlocks.