You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Rici Lake <ri...@ricilake.net> on 2005/04/26 23:18:41 UTC

Moving on to input filters :)

The docstring in util_filter.h for ap_get_brigade says:

  * Get the current bucket brigade from the next filter on the filter
  * stack.  The filter returns an apr_status_t value.  If the bottom-most
  * filter doesn't read from the network, then ::AP_NOBODY_READ is 
returned.
  * The bucket brigade will be empty when there is nothing left to get.

The best reference for the behaviour of ap_get_brigade would appear to 
be core_input_filter. Its behaviour differs from this docstring; in 
particular, end of input is (sometimes) signalled with an eos bucket 
being returned in the brigade.

I propose clarifying the last line of that docstring:

* If block is APR_NONBLOCK_READ and no data is currently available,
* the brigade will be empty on return, and the function will return
* APR_SUCCESS.
*
* If no more data is available, the brigade will contain an eos bucket
* on return, and the function will return APR_SUCCESS. A subsequent
* call to ap_get_brigade will return APR_EOF without altering the
* brigade.
*
* In no case will APR_EAGAIN be returned. Other error conditions may
* be returned, with or without data in the brigade.
*
* Input filters are expected to conform to the above interface.

The above is not quite the behaviour of core_input_filter, but I think
it could be with a few minor tweaks, none of which will cost cycles.
As I read the code, it behaves as follows;

In MODE_READBYTES:

With APR_BLOCK_READ, on EOF the filter will first return
   a brigade containing an EOS bucket, with status APR_SUCCESS.
   A subsequent call to the filter will return an empty
   brigade with status APR_EOF. It should not otherwise
   return an empty brigade.

With APR_NONBLOCK_READ, an empty brigade will be returned with
   status APR_SUCCESS if the the socket has no data ready OR
   if an EOF was encountered. There appears to be no way to
   distinguish between these cases other than by doing a blocking
   read. (Note 1)

In MODE_EXHAUSTIVE:
   regardless of read blocking, the socket bucket is simply
   appended to the returned brigade, followed by an EOS bucket.
   APR_SUCCESS is always returned.

In MODE_GETLINE and MODE_SPECULATIVE:

With APR_BLOCK_READ, on EOF the filter will return an empty
   brigade with status APR_SUCCESS. A subsequent call to the filter
   will return an empty brigade with status APR_EOF. (Notes 2 and 3)

With APR_NONBLOCK_READ, the filter will behave as in MODE_READBYTES.


Note 1:
   I believe this behaviour to be incorrect. Line 285 of
   server/core_filters.c is:

         else if (block == APR_BLOCK_READ && len == 0) {

   At this point, it has already checked for APR_EAGAIN and
   error status returns. Consequently, a zero-length read in
   either blocking or non-blocking mode ought to be an EOF.
   I think this should be changed to:

         else if (len == 0) {

   which would insert the EOS bucket in the case of a non-blocking
   get_brigade. However, I don't know if it would confuse consumers
   who were not expecting it.

   It would actually probably be simpler to insert the eos bucket
   in the ctx->b brigade when it is built, rather than inserting
   it in response to various eof-like conditions later on in the
   code.

Note 2:
   In MODE_GETLINE, no attempt is made to insert an eos bucket in
   either blocking or non-blocking mode; in the case of EOF, I
   believe that the socket bucket will remove itself, leading to
   an eventual return of APR_EOF. This seems inconsistent with
   MODE_READBYTES.

Note 3:
   In MODE_SPECULATIVE, the eos bucket is deliberately not created.
   A comment says that the next will return an EOS bucket, but
   as far as I can see, once ctx->b has been emptied, as it will
   have been by the call to apr_bucket_delete at line 294, the
   next call to the filter will return an empty bucket with
   status APR_EOF.

Finally:
   I think there is a possibility that the socket bucket's read
   function will lose data, if the underlying call to apr_socket_recv
   returns a non-zero-length buffer along with a non-APR_SUCCESS
   status. However, this may not actually ever happen. It would
   only really be serious in the case of an APR_EAGAIN return.