Ejemplo n.º 1
0
APU_DECLARE(apr_status_t) apr_brigade_puts(apr_bucket_brigade *bb,
                                           apr_brigade_flush flush, void *ctx,
                                           const char *str)
{
    apr_size_t len = strlen(str);
    apr_bucket *bkt = APR_BRIGADE_LAST(bb);
    if (!APR_BRIGADE_EMPTY(bb) && APR_BUCKET_IS_HEAP(bkt)) {
        /* If there is enough space available in a heap bucket
         * at the end of the brigade, copy the string directly
         * into the heap bucket
         */
        apr_bucket_heap *h = bkt->data;
        apr_size_t bytes_avail = h->alloc_len - bkt->length;

        if (bytes_avail >= len) {
            char *buf = h->base + bkt->start + bkt->length;
            memcpy(buf, str, len);
            bkt->length += len;
            return APR_SUCCESS;
        }
    }

    /* If the string could not be copied into an existing heap
     * bucket, delegate the work to apr_brigade_write(), which
     * knows how to grow the brigade
     */
    return apr_brigade_write(bb, flush, ctx, str, len);
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
static apr_status_t append_bucket(h2_bucket_beam *beam, 
                                  apr_bucket *bred,
                                  apr_read_type_e block,
                                  apr_pool_t *pool,
                                  h2_beam_lock *pbl)
{
    const char *data;
    apr_size_t len;
    apr_off_t space_left = 0;
    apr_status_t status;
    
    if (APR_BUCKET_IS_METADATA(bred)) {
        if (APR_BUCKET_IS_EOS(bred)) {
            beam->closed = 1;
        }
        APR_BUCKET_REMOVE(bred);
        H2_BLIST_INSERT_TAIL(&beam->red, bred);
        return APR_SUCCESS;
    }
    else if (APR_BUCKET_IS_FILE(bred)) {
        /* file bucket lengths do not really count */
    }
    else {
        space_left = calc_space_left(beam);
        if (space_left > 0 && bred->length == ((apr_size_t)-1)) {
            const char *data;
            status = apr_bucket_read(bred, &data, &len, APR_BLOCK_READ);
            if (status != APR_SUCCESS) {
                return status;
            }
        }
        
        if (space_left < bred->length) {
            status = r_wait_space(beam, block, pbl, &space_left);
            if (status != APR_SUCCESS) {
                return status;
            }
            if (space_left <= 0) {
                return APR_EAGAIN;
            }
        }
        /* space available, maybe need bucket split */
    }
    

    /* The fundamental problem is that reading a red bucket from
     * a green thread is a total NO GO, because the bucket might use
     * its pool/bucket_alloc from a foreign thread and that will
     * corrupt. */
    status = APR_ENOTIMPL;
    if (beam->closed && bred->length > 0) {
        status = APR_EOF;
    }
    else if (APR_BUCKET_IS_TRANSIENT(bred)) {
        /* this takes care of transient buckets and converts them
         * into heap ones. Other bucket types might or might not be
         * affected by this. */
        status = apr_bucket_setaside(bred, pool);
    }
    else if (APR_BUCKET_IS_HEAP(bred)) {
        /* For heap buckets read from a green thread is fine. The
         * data will be there and live until the bucket itself is
         * destroyed. */
        status = APR_SUCCESS;
    }
    else if (APR_BUCKET_IS_POOL(bred)) {
        /* pool buckets are bastards that register at pool cleanup
         * to morph themselves into heap buckets. That may happen anytime,
         * even after the bucket data pointer has been read. So at
         * any time inside the green thread, the pool bucket memory
         * may disappear. yikes. */
        status = apr_bucket_read(bred, &data, &len, APR_BLOCK_READ);
        if (status == APR_SUCCESS) {
            apr_bucket_heap_make(bred, data, len, NULL);
        }
    }
    else if (APR_BUCKET_IS_FILE(bred)) {
        /* For file buckets the problem is their internal readpool that
         * is used on the first read to allocate buffer/mmap.
         * Since setting aside a file bucket will de-register the
         * file cleanup function from the previous pool, we need to
         * call that from a red thread. 
         * Additionally, we allow callbacks to prevent beaming file
         * handles across. The use case for this is to limit the number 
         * of open file handles and rather use a less efficient beam
         * transport. */
        apr_file_t *fd = ((apr_bucket_file *)bred->data)->fd;
        int can_beam = 1;
        if (beam->last_beamed != fd && beam->can_beam_fn) {
            can_beam = beam->can_beam_fn(beam->can_beam_ctx, beam, fd);
        }
        if (can_beam) {
            beam->last_beamed = fd;
            status = apr_bucket_setaside(bred, pool);
        }
        /* else: enter ENOTIMPL case below */
    }
    
    if (status == APR_ENOTIMPL) {
        /* we have no knowledge about the internals of this bucket,
         * but hope that after read, its data stays immutable for the
         * lifetime of the bucket. (see pool bucket handling above for
         * a counter example).
         * We do the read while in a red thread, so that the bucket may
         * use pools/allocators safely. */
        if (space_left < APR_BUCKET_BUFF_SIZE) {
            space_left = APR_BUCKET_BUFF_SIZE;
        }
        if (space_left < bred->length) {
            apr_bucket_split(bred, space_left);
        }
        status = apr_bucket_read(bred, &data, &len, APR_BLOCK_READ);
        if (status == APR_SUCCESS) {
            status = apr_bucket_setaside(bred, pool);
        }
    }
    
    if (status != APR_SUCCESS && status != APR_ENOTIMPL) {
        return status;
    }
    
    APR_BUCKET_REMOVE(bred);
    H2_BLIST_INSERT_TAIL(&beam->red, bred);
    beam->sent_bytes += bred->length;
    
    return APR_SUCCESS;
}