httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bri...@apache.org
Subject cvs commit: httpd-2.0 CHANGES
Date Wed, 28 Aug 2002 18:37:49 GMT
brianp      2002/08/28 11:37:49

  Modified:    server   protocol.c
               .        CHANGES
  Log:
  Changed the content-length filter to allow streaming delivery
  of content to clients
  
  With this new code, the C-L filter will compute a content-length
  if there's an EOS in the first brigade passed to it.  For normal
  static file requests, the response header will still include a C-L.
  If there's no EOS in the first brigade passed to the C-L filter,
  the filter will give up on setting the C-L header, in favor of
  passing the data on to the next filter in a timely manner.
  
  Revision  Changes    Path
  1.116     +64 -115   httpd-2.0/server/protocol.c
  
  Index: protocol.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/server/protocol.c,v
  retrieving revision 1.115
  retrieving revision 1.116
  diff -u -r1.115 -r1.116
  --- protocol.c	13 Aug 2002 14:27:39 -0000	1.115
  +++ protocol.c	28 Aug 2002 18:37:48 -0000	1.116
  @@ -1200,9 +1200,10 @@
   }
   
   struct content_length_ctx {
  -    apr_bucket_brigade *saved;
  -    int compute_len;
  -    apr_size_t curr_len;
  +    int data_sent;  /* true if the C-L filter has already sent at
  +                     * least one bucket on to the next output filter
  +                     * for this request
  +                     */
   };
   
   /* This filter computes the content length, but it also computes the number
  @@ -1214,140 +1215,88 @@
   {
       request_rec *r = f->r;
       struct content_length_ctx *ctx;
  -    apr_status_t rv;
       apr_bucket *e;
  -    int eos = 0, flush = 0, partial_send_okay = 0;
  -    apr_bucket_brigade *more, *split;
  +    int eos = 0;
       apr_read_type_e eblock = APR_NONBLOCK_READ;
   
  -    ctx = f->ctx;
  -    if (!ctx) { /* first time through */
  -        f->ctx = ctx = apr_pcalloc(r->pool, sizeof(struct content_length_ctx));
  -        ctx->compute_len = 1;   /* Assume we will compute the length */
  -        ctx->saved = apr_brigade_create(r->pool, f->c->bucket_alloc);
  -    }
  +    int can_send_content_length;
   
  -    /* Humm, is this check the best it can be?
  -     * - protocol >= HTTP/1.1 implies support for chunking
  -     * - non-keepalive implies the end of byte stream will be signaled
  -     *    by a connection close
  -     * In both cases, we can send bytes to the client w/o needing to
  -     * compute content-length.
  -     * Todo:
  -     * We should be able to force connection close from this filter
  -     * when we see we are buffering too much.
  +    /* We can only set a C-L in the response header if we
  +     * haven't already sent any buckets on to the next
  +     * output filter for this request.
        */
  -    if ((r->proto_num >= HTTP_VERSION(1, 1)) ||
  -        (r->connection->keepalive == AP_CONN_CLOSE)) {
  -        partial_send_okay = 1;
  +    ctx = f->ctx;
  +    if (!ctx) {
  +        f->ctx = ctx = apr_palloc(r->pool, sizeof(struct content_length_ctx));
  +        ctx->data_sent = 0;
  +        can_send_content_length = 1;
  +    }
  +    else {
  +        can_send_content_length = (ctx->data_sent == 0);
       }
   
  -    more = b;
  -    while (more) {
  -        b = more;
  -        more = NULL;
  -        split = NULL;
  -        flush = 0;
  -
  -        e = APR_BRIGADE_FIRST(b);
  -        while (e != APR_BRIGADE_SENTINEL(b)) {
  -            const char *ignored;
  +    /* Loop through this set of buckets to compute their length
  +     */
  +    e = APR_BRIGADE_FIRST(b);
  +    while (e != APR_BRIGADE_SENTINEL(b)) {
  +        if (APR_BUCKET_IS_EOS(e)) {
  +            eos = 1;
  +            break;
  +        }
  +        if (e->length == -1) {
               apr_size_t len;
  -            len = 0;
  -            if (APR_BUCKET_IS_EOS(e)) {
  -                eos = 1;
  -                break;
  -            }
  -            else if (APR_BUCKET_IS_FLUSH(e)) {
  -                if (partial_send_okay) {
  -                    split = b;
  -                    more = apr_brigade_split(b, APR_BUCKET_NEXT(e));
  -                    break;
  -                }
  -            }
  -            else if ((ctx->curr_len > 4 * AP_MIN_BYTES_TO_WRITE)) {
  -                /* If we've accumulated more than 4xAP_MIN_BYTES_TO_WRITE and
  -                 * the client supports chunked encoding, send what we have
  -                 * and come back for more.
  +            const char *ignored;
  +            apr_status_t rv;
  +
  +            /* This is probably a pipe bucket.  Send everything
  +             * prior to this, and then read the data for this bucket.
  +             */
  +            rv = apr_bucket_read(e, &ignored, &len, eblock);
  +            if (rv == APR_SUCCESS) {
  +                /* Attempt a nonblocking read next time through */
  +                eblock = APR_NONBLOCK_READ;
  +                r->bytes_sent += len;
  +            }
  +            else if (APR_STATUS_IS_EAGAIN(rv)) {
  +                /* Output everything prior to this bucket, and
  +                 * do a blocking read
                    */
  -                if (partial_send_okay) {
  -                    split = b;
  -                    more = apr_brigade_split(b, e);
  -                    break;
  -                }
  -            }
  -            if (e->length == -1) { /* if length unknown */
  -                rv = apr_bucket_read(e, &ignored, &len, eblock);
  -                if (rv == APR_SUCCESS) {
  -                    /* Attempt a nonblocking read next time through */
  -                    eblock = APR_NONBLOCK_READ;
  -                }
  -                else if (APR_STATUS_IS_EAGAIN(rv)) {
  -                    /* Make the next read blocking.  If the client supports
  -                     * chunked encoding, flush the filter stack to the network.
  -                     */
  -                    eblock = APR_BLOCK_READ;
  -                    if (partial_send_okay) {
  -                        split = b;
  -                        more = apr_brigade_split(b, e);
  -                        flush = 1;
  -                        break;
  +                if (e != APR_BRIGADE_FIRST(b)) {
  +                    apr_bucket_brigade *split = apr_brigade_split(b, e);
  +                    rv = ap_pass_brigade(f->next, b);
  +                    if (rv != APR_SUCCESS) {
  +                        apr_brigade_destroy(split);
  +                        return rv;
                       }
  -                    continue;
  -                }
  -                else if (rv != APR_EOF) {
  -                    ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
  -                                  "ap_content_length_filter: "
  -                                  "apr_bucket_read() failed");
  -                    return rv;
  +                    b = split;
  +                    e = APR_BRIGADE_FIRST(b);
  +                    can_send_content_length = 0;
  +                    ctx->data_sent = 1;
                   }
  +                eblock = APR_BLOCK_READ;
  +                continue;
               }
               else {
  -                len = e->length;
  +                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
  +                              "ap_content_length_filter: "
  +                              "apr_bucket_read() failed");
  +                return rv;
               }
  -
  -            ctx->curr_len += len;
  -            r->bytes_sent += len;
  -            e = APR_BUCKET_NEXT(e);
           }
  -
  -        if (split) {
  -            ctx->compute_len = 0;  /* Ooops, can't compute the length now */
  -            ctx->curr_len = 0;
  -
  -            APR_BRIGADE_PREPEND(split, ctx->saved);
  -
  -            if (flush) {
  -                rv = ap_fflush(f->next, split);
  -            }
  -            else {
  -                rv = ap_pass_brigade(f->next, split);
  -            }
  -
  -            if (rv != APR_SUCCESS)
  -                return rv;
  +        else {
  +            r->bytes_sent += e->length;
           }
  +        e = APR_BUCKET_NEXT(e);
       }
   
  -    if ((ctx->curr_len < AP_MIN_BYTES_TO_WRITE) && !eos) {
  -        return ap_save_brigade(f, &ctx->saved, &b,
  -                               (r->main) ? r->main->pool : r->pool);
  -    }
  -
  -    if (ctx->compute_len) {
  -        /* save the brigade; we can't pass any data to the next
  -         * filter until we have the entire content length
  +    /* If we've now seen the entire response and it's otherwise
  +     * okay to set the C-L in the response header, do so now:
            */
  -        if (!eos) {
  -            return ap_save_brigade(f, &ctx->saved, &b, r->pool);
  -        }
  -
  +    if (can_send_content_length && eos) {
           ap_set_content_length(r, r->bytes_sent);
       }
   
  -    APR_BRIGADE_PREPEND(b, ctx->saved);
  -
  -    ctx->curr_len = 0;
  +    ctx->data_sent = 1;
       return ap_pass_brigade(f->next, b);
   }
   
  
  
  
  1.904     +5 -0      httpd-2.0/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/CHANGES,v
  retrieving revision 1.903
  retrieving revision 1.904
  diff -u -r1.903 -r1.904
  --- CHANGES	28 Aug 2002 18:23:05 -0000	1.903
  +++ CHANGES	28 Aug 2002 18:37:48 -0000	1.904
  @@ -1,4 +1,9 @@
   Changes with Apache 2.0.41
  +
  +  *) The content-length filter no longer tries to buffer up
  +     the entire output of a long-running request before sending
  +     anything to the client.  [Brian Pane]
  +
     *) Win32: Lower the default stack size from 1MB to 256K. This will
        allow around 8000 threads to be started per child process. 
        'EDITBIN /STACK:size apache.exe' can be used to change this 
  
  
  

Mime
View raw message