Commit 3e161cb3 authored by Stefan Eissing's avatar Stefan Eissing
Browse files

backport of mod_http2 v1.4.2

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1735239 13f79535-47bb-0310-9956-ffa450edef68
parent f43d0359
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -2,6 +2,9 @@

Changes with Apache 2.4.19

  *) mod_http2: fixes problem with wrong lifetime of file buckets on main
     connection. [Stefan Eissing]
     
  *) mod_http2: fixes incorrect denial of requests without :authority header.
     [Stefan Eissing]
     
+1 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ struct h2_session;
/** End Of HTTP/2 SESSION (H2EOC) bucket */
extern const apr_bucket_type_t h2_bucket_type_eoc;

#define H2_BUCKET_IS_H2EOC(e)     (e->type == &h2_bucket_type_eoc)

apr_bucket * h2_bucket_eoc_make(apr_bucket *b, 
                                struct h2_session *session);
+1 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ struct h2_stream;
/** End Of HTTP/2 STREAM (H2EOS) bucket */
extern const apr_bucket_type_t h2_bucket_type_eos;

#define H2_BUCKET_IS_H2EOS(e)     (e->type == &h2_bucket_type_eos)

apr_bucket *h2_bucket_eos_make(apr_bucket *b, struct h2_stream *stream);

+9 −6
Original line number Diff line number Diff line
@@ -261,7 +261,7 @@ conn_rec *h2_slave_create(conn_rec *master, apr_pool_t *parent,
    }
    apr_pool_create_ex(&pool, parent, NULL, allocator);
    apr_pool_tag(pool, "h2_slave_conn");
    apr_allocator_owner_set(allocator, parent);
    apr_allocator_owner_set(allocator, pool);
    
    c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec));
    if (c == NULL) {
@@ -309,15 +309,18 @@ conn_rec *h2_slave_create(conn_rec *master, apr_pool_t *parent,

void h2_slave_destroy(conn_rec *slave, apr_allocator_t **pallocator)
{
    apr_pool_t *parent;
    apr_allocator_t *allocator = apr_pool_allocator_get(slave->pool);
    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, slave,
                  "h2_slave_conn(%ld): destroy (task=%s)", slave->id,
                  apr_table_get(slave->notes, H2_TASK_ID_NOTE));
    apr_pool_destroy(slave->pool);
    if (pallocator) {
    /* Attache the allocator to the parent pool and return it for
     * reuse, otherwise the own is still the slave pool and it will
     * get destroyed with it. */
    parent = apr_pool_parent_get(slave->pool);
    if (pallocator && parent) {
        apr_allocator_owner_set(allocator, parent);
        *pallocator = allocator;
    }
    else {
        apr_allocator_destroy(allocator);
    }
    apr_pool_destroy(slave->pool);
}
+143 −71
Original line number Diff line number Diff line
@@ -14,16 +14,18 @@
 */

#include <assert.h>

#include <apr_strings.h>
#include <ap_mpm.h>

#include <httpd.h>
#include <http_core.h>
#include <http_log.h>
#include <http_connection.h>
#include <http_request.h>

#include "h2_private.h"
#include "h2_bucket_eoc.h"
#include "h2_bucket_eos.h"
#include "h2_config.h"
#include "h2_conn_io.h"
#include "h2_h2.h"
@@ -43,14 +45,92 @@
 * which seems to create less TCP packets overall
 */
#define WRITE_SIZE_MAX        (TLS_DATA_MAX - 100) 

#define WRITE_BUFFER_SIZE     (5*WRITE_SIZE_MAX)


static void h2_conn_io_bb_log(conn_rec *c, int stream_id, int level, 
                              const char *tag, apr_bucket_brigade *bb)
{
    char buffer[16 * 1024];
    const char *line = "(null)";
    apr_size_t bmax = sizeof(buffer)/sizeof(buffer[0]);
    int off = 0;
    apr_bucket *b;
    
    if (bb) {
        memset(buffer, 0, bmax--);
        for (b = APR_BRIGADE_FIRST(bb); 
             bmax && (b != APR_BRIGADE_SENTINEL(bb));
             b = APR_BUCKET_NEXT(b)) {
            
            if (APR_BUCKET_IS_METADATA(b)) {
                if (APR_BUCKET_IS_EOS(b)) {
                    off += apr_snprintf(buffer+off, bmax-off, "eos ");
                }
                else if (APR_BUCKET_IS_FLUSH(b)) {
                    off += apr_snprintf(buffer+off, bmax-off, "flush ");
                }
                else if (AP_BUCKET_IS_EOR(b)) {
                    off += apr_snprintf(buffer+off, bmax-off, "eor ");
                }
                else if (H2_BUCKET_IS_H2EOC(b)) {
                    off += apr_snprintf(buffer+off, bmax-off, "h2eoc ");
                }
                else if (H2_BUCKET_IS_H2EOS(b)) {
                    off += apr_snprintf(buffer+off, bmax-off, "h2eos ");
                }
                else {
                    off += apr_snprintf(buffer+off, bmax-off, "meta(unknown) ");
                }
            }
            else {
                const char *btype = "data";
                if (APR_BUCKET_IS_FILE(b)) {
                    btype = "file";
                }
                else if (APR_BUCKET_IS_PIPE(b)) {
                    btype = "pipe";
                }
                else if (APR_BUCKET_IS_SOCKET(b)) {
                    btype = "socket";
                }
                else if (APR_BUCKET_IS_HEAP(b)) {
                    btype = "heap";
                }
                else if (APR_BUCKET_IS_TRANSIENT(b)) {
                    btype = "transient";
                }
                else if (APR_BUCKET_IS_IMMORTAL(b)) {
                    btype = "immortal";
                }
#if APR_HAS_MMAP
                else if (APR_BUCKET_IS_MMAP(b)) {
                    btype = "mmap";
                }
#endif
                else if (APR_BUCKET_IS_POOL(b)) {
                    btype = "pool";
                }
                
                off += apr_snprintf(buffer+off, bmax-off, "%s[%ld] ", 
                                    btype, 
                                    (long)(b->length == ((apr_size_t)-1)? 
                                           -1 : b->length));
            }
        }
        line = *buffer? buffer : "(empty)";
    }
    /* Intentional no APLOGNO */
    ap_log_cerror(APLOG_MARK, level, 0, c, "bb_dump(%ld-%d)-%s: %s", 
                  c->id, stream_id, tag, line);

}

apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, 
                             const h2_config *cfg, 
                             apr_pool_t *pool)
{
    io->connection         = c;
    io->c             = c;
    io->output        = apr_brigade_create(pool, c->bucket_alloc);
    io->buflen        = 0;
    io->is_tls        = h2_h2_is_tls(c);
@@ -65,8 +145,9 @@ apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c,
    }
    
    if (io->is_tls) {
        /* That is where we start with, 
         * see https://issues.apache.org/jira/browse/TS-2503 */
        /* This is what we start with, 
         * see https://issues.apache.org/jira/browse/TS-2503 
         */
        io->warmup_size    = h2_config_geti64(cfg, H2_CONF_TLS_WARMUP_SIZE);
        io->cooldown_usecs = (h2_config_geti(cfg, H2_CONF_TLS_COOLDOWN_SECS) 
                              * APR_USEC_PER_SEC);
@@ -79,9 +160,10 @@ apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c,
    }

    if (APLOGctrace1(c)) {
        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->connection,
                      "h2_conn_io(%ld): init, buffering=%d, warmup_size=%ld, cd_secs=%f",
                      io->connection->id, io->buffer_output, (long)io->warmup_size,
        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->c,
                      "h2_conn_io(%ld): init, buffering=%d, warmup_size=%ld, "
                      "cd_secs=%f", io->c->id, io->buffer_output, 
                      (long)io->warmup_size,
                      ((float)io->cooldown_usecs/APR_USEC_PER_SEC));
    }

@@ -110,16 +192,17 @@ static apr_status_t pass_out(apr_bucket_brigade *bb, void *ctx)
    }
    
    ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_WRITE, c);
    status = apr_brigade_length(bb, 0, &bblen);
    if (status == APR_SUCCESS) {
        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03044)
                      "h2_conn_io(%ld): pass_out brigade %ld bytes",
                      c->id, (long)bblen);
    apr_brigade_length(bb, 0, &bblen);
    h2_conn_io_bb_log(c, 0, APLOG_TRACE2, "master conn pass", bb);
    status = ap_pass_brigade(c->output_filters, bb);
    if (status == APR_SUCCESS && pctx->io) {
        pctx->io->bytes_written += (apr_size_t)bblen;
        pctx->io->last_write = apr_time_now();
    }
    if (status != APR_SUCCESS) {
        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, APLOGNO(03044)
                      "h2_conn_io(%ld): pass_out brigade %ld bytes",
                      c->id, (long)bblen);
    }
    apr_brigade_cleanup(bb);
    return status;
@@ -141,17 +224,17 @@ static apr_status_t bucketeer_buffer(h2_conn_io *io)
        /* long time not written, reset write size */
        io->write_size = WRITE_SIZE_INITIAL;
        io->bytes_written = 0;
        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->connection,
        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->c,
                      "h2_conn_io(%ld): timeout write size reset to %ld", 
                      (long)io->connection->id, (long)io->write_size);
                      (long)io->c->id, (long)io->write_size);
    }
    else if (io->write_size < WRITE_SIZE_MAX 
             && io->bytes_written >= io->warmup_size) {
        /* connection is hot, use max size */
        io->write_size = WRITE_SIZE_MAX;
        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->connection,
        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->c,
                      "h2_conn_io(%ld): threshold reached, write size now %ld", 
                      (long)io->connection->id, (long)io->write_size);
                      (long)io->c->id, (long)io->write_size);
    }
    
    bcount = (int)(remaining / io->write_size);
@@ -177,50 +260,41 @@ apr_status_t h2_conn_io_writeb(h2_conn_io *io, apr_bucket *b)
    return APR_SUCCESS;
}

static apr_status_t h2_conn_io_flush_int(h2_conn_io *io, int force, int eoc)
static apr_status_t h2_conn_io_flush_int(h2_conn_io *io, int flush, int eoc)
{
    if (io->buflen > 0 || !APR_BRIGADE_EMPTY(io->output)) {
    pass_out_ctx ctx;
    apr_bucket *b;
    
    if (io->buflen == 0 && APR_BRIGADE_EMPTY(io->output)) {
        return APR_SUCCESS;
    }
        
    if (io->buflen > 0) {
        /* something in the buffer, put it in the output brigade */
            ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->connection,
                          "h2_conn_io: flush, flushing %ld bytes", (long)io->buflen);
        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->c,
                      "h2_conn_io: flush, flushing %ld bytes", 
                      (long)io->buflen);
        bucketeer_buffer(io);
    }
    
        if (force) {
            APR_BRIGADE_INSERT_TAIL(io->output,
                                    apr_bucket_flush_create(io->output->bucket_alloc));
    if (flush) {
        b = apr_bucket_flush_create(io->c->bucket_alloc);
        APR_BRIGADE_INSERT_TAIL(io->output, b);
    }
    
        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->connection,
                      "h2_conn_io: flush");
        /* Send it out */
    ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->c, "h2_conn_io: flush");
    io->buflen = 0;
        ctx.c = io->connection;
    ctx.c = io->c;
    ctx.io = eoc? NULL : io;
    
    return pass_out(io->output, &ctx);
    /* no more access after this, as we might have flushed an EOC bucket
     * that de-allocated us all. */
}
    return APR_SUCCESS;
}

apr_status_t h2_conn_io_pass(h2_conn_io *io, int flush)
{
    return h2_conn_io_flush_int(io, flush, 0);
}

apr_status_t h2_conn_io_flush(h2_conn_io *io)
{
    /* make sure we always write a flush, even if our buffers are empty.
     * We want to flush not only our buffers, but alse ones further down
     * the connection filters. */
    apr_bucket *b = apr_bucket_flush_create(io->connection->bucket_alloc);
    APR_BRIGADE_INSERT_TAIL(io->output, b);
    return h2_conn_io_flush_int(io, 0, 0);
    return h2_conn_io_flush_int(io, 1, 0);
}

apr_status_t h2_conn_io_consider_pass(h2_conn_io *io)
@@ -228,20 +302,18 @@ apr_status_t h2_conn_io_consider_pass(h2_conn_io *io)
    apr_off_t len = 0;
    
    if (!APR_BRIGADE_EMPTY(io->output)) {
        apr_brigade_length(io->output, 0, &len);
        len = h2_brigade_mem_size(io->output);
    }
    len += io->buflen;
    if (len >= WRITE_BUFFER_SIZE) {
        return h2_conn_io_pass(io, 0);
        return h2_conn_io_flush_int(io, 1, 0);
    }
    return APR_SUCCESS;
}

apr_status_t h2_conn_io_write_eoc(h2_conn_io *io, h2_session *session)
{
    apr_bucket *b = h2_bucket_eoc_create(io->connection->bucket_alloc, session);
    APR_BRIGADE_INSERT_TAIL(io->output, b);
    b = apr_bucket_flush_create(io->connection->bucket_alloc);
    apr_bucket *b = h2_bucket_eoc_create(io->c->bucket_alloc, session);
    APR_BRIGADE_INSERT_TAIL(io->output, b);
    return h2_conn_io_flush_int(io, 0, 1);
}
@@ -252,20 +324,20 @@ apr_status_t h2_conn_io_write(h2_conn_io *io,
    apr_status_t status = APR_SUCCESS;
    pass_out_ctx ctx;
    
    ctx.c = io->connection;
    ctx.c = io->c;
    ctx.io = io;
    if (io->bufsize > 0) {
        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->connection,
        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->c,
                      "h2_conn_io: buffering %ld bytes", (long)length);
                      
        if (!APR_BRIGADE_EMPTY(io->output)) {
            status = h2_conn_io_pass(io, 0);
            status = h2_conn_io_flush_int(io, 0, 0);
        }
        
        while (length > 0 && (status == APR_SUCCESS)) {
            apr_size_t avail = io->bufsize - io->buflen;
            if (avail <= 0) {
                h2_conn_io_pass(io, 0);
                status = h2_conn_io_flush_int(io, 0, 0);
            }
            else if (length > avail) {
                memcpy(io->buffer + io->buflen, buf, avail);
@@ -283,7 +355,7 @@ apr_status_t h2_conn_io_write(h2_conn_io *io,
        
    }
    else {
        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, status, io->connection,
        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, status, io->c,
                      "h2_conn_io: writing %ld bytes to brigade", (long)length);
        status = apr_brigade_write(io->output, pass_out, &ctx, buf, length);
    }
Loading