Commit 0e4bfd98 authored by Stefan Eissing's avatar Stefan Eissing
Browse files

On the trunk:

  *) mod_http2: fix for possible page fault when stream is resumed during 
     session shutdown. [sidney-j-r-m (github)]
     
  *) mod_http2: fix for h2 session ignoring new responses while already
     open streams continue to have data available. [Stefan Eissing]
     



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1777160 13f79535-47bb-0310-9956-ffa450edef68
parent fc225494
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
                                                         -*- coding: utf-8 -*-
Changes with Apache 2.5.0

  *) mod_http2: fix for possible page fault when stream is resumed during 
     session shutdown. [sidney-j-r-m (github)]
     
  *) mod_http2: fix for h2 session ignoring new responses while already
     open streams continue to have data available. [Stefan Eissing]
     
  *) mod_remoteip: Add support for PROXY protocol (code donated by Cloudzilla).
     Add ability for PROXY protocol processing to be optional to donated code.
     See also: http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
+53 −27
Original line number Diff line number Diff line
@@ -132,8 +132,9 @@ apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c,
    io->output         = apr_brigade_create(c->pool, c->bucket_alloc);
    io->is_tls         = h2_h2_is_tls(c);
    io->buffer_output  = io->is_tls;
    io->pass_threshold = (apr_size_t)h2_config_geti64(cfg, H2_CONF_STREAM_MAX_MEM) / 2;
    io->pass_threshold = (apr_size_t)h2_config_geti64(cfg, H2_CONF_STREAM_MAX_MEM);
    io->flush_factor   = h2_config_geti(cfg, H2_CONF_TLS_FLUSH_COUNT);
    io->speed_factor   = 1.0;

    if (io->is_tls) {
        /* This is what we start with, 
@@ -256,6 +257,7 @@ static void check_write_size(h2_conn_io *io)
        /* long time not written, reset write size */
        io->write_size = WRITE_SIZE_INITIAL;
        io->bytes_written = 0;
        io->speed_factor = 1.0;
        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->c,
                      "h2_conn_io(%ld): timeout write size reset to %ld", 
                      (long)io->c->id, (long)io->write_size);
@@ -325,7 +327,34 @@ static apr_status_t pass_output(h2_conn_io *io, int flush,

apr_status_t h2_conn_io_flush(h2_conn_io *io)
{
    return pass_output(io, 1, NULL);
    apr_time_t start = 0;
    apr_status_t status;
    
    if (io->needs_flush > 0) {
        /* this is a buffer size triggered flush, let's measure how
         * long it takes and try to adjust our speed factor accordingly */
        start = apr_time_now();
    }
    status = pass_output(io, 1, NULL);
    check_write_size(io);
    if (start && status == APR_SUCCESS) {
        apr_time_t duration = apr_time_now() - start;
        if (duration < apr_time_from_msec(100)) {
            io->speed_factor *= 1.0 + ((apr_time_from_msec(100) - duration) 
                                        / (float)apr_time_from_msec(100));
            ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, io->c,
                          "h2_conn_io(%ld): incr speed_factor to %f",
                          io->c->id, io->speed_factor);
        }
        else if (duration > apr_time_from_msec(200)) {
            io->speed_factor *= 0.5;
            ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, io->c,
                          "h2_conn_io(%ld): decr speed_factor to %f",
                          io->c->id, io->speed_factor);
        }
    }
    io->needs_flush = -1;
    return status;
}

apr_status_t h2_conn_io_write_eoc(h2_conn_io *io, h2_session *session)
@@ -367,6 +396,7 @@ apr_status_t h2_conn_io_write(h2_conn_io *io, const char *data, size_t length)
    else {
        status = apr_brigade_write(io->output, NULL, NULL, data, length);
    }
    io->needs_flush = -1;
    return status;
}

@@ -375,7 +405,6 @@ apr_status_t h2_conn_io_pass(h2_conn_io *io, apr_bucket_brigade *bb)
    apr_bucket *b;
    apr_status_t status = APR_SUCCESS;
    
    check_write_size(io);
    while (!APR_BRIGADE_EMPTY(bb) && status == APR_SUCCESS) {
        b = APR_BRIGADE_FIRST(bb);
        
@@ -418,24 +447,21 @@ apr_status_t h2_conn_io_pass(h2_conn_io *io, apr_bucket_brigade *bb)
            APR_BRIGADE_INSERT_TAIL(io->output, b);
        }
    }
    io->needs_flush = -1;
    return status;
}

    if (status == APR_SUCCESS) {
        if (!APR_BRIGADE_EMPTY(io->output)) {
int h2_conn_io_needs_flush(h2_conn_io *io)
{
    if (io->needs_flush < 0) {
        apr_off_t len;
            if (io->buffer_output) {
        apr_brigade_length(io->output, 0, &len);
                if (len >= (io->flush_factor * io->write_size)) {
                    return pass_output(io, 1, NULL);
                }
            }
            else {
                len = h2_brigade_mem_size(io->output);
                if (len >= io->pass_threshold) {
                    return pass_output(io, 0, NULL);
        if (len > (io->pass_threshold * io->flush_factor * io->speed_factor)) {
            /* don't want to keep too much around */
            io->needs_flush = 1;
            return 1;
        }
        io->needs_flush = 0;
    }
    return 0;
}
    }
    return status;
}
+5 −1
Original line number Diff line number Diff line
@@ -39,8 +39,10 @@ typedef struct {
    apr_int64_t bytes_written;
    
    int buffer_output;
    int needs_flush;
    apr_size_t pass_threshold;
    int flush_factor;
    float flush_factor;
    float speed_factor;
    
    char *scratch;
    apr_size_t ssize;
@@ -74,4 +76,6 @@ apr_status_t h2_conn_io_write_eoc(h2_conn_io *io, struct h2_session *session);
 */
apr_status_t h2_conn_io_flush(h2_conn_io *io);

int h2_conn_io_needs_flush(h2_conn_io *io);

#endif /* defined(__mod_h2__h2_conn_io__) */
+42 −12
Original line number Diff line number Diff line
@@ -47,6 +47,8 @@
#include "h2_workers.h"


static apr_status_t dispatch_master(h2_session *session);

static int h2_session_status_from_apr_status(apr_status_t rv)
{
    if (rv == APR_SUCCESS) {
@@ -238,6 +240,17 @@ static ssize_t send_cb(nghttp2_session *ngh2,
    
    (void)ngh2;
    (void)flags;
    if (h2_conn_io_needs_flush(&session->io)) {
        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
                      "h2_session(%ld): blocking due to io flush",
                      session->id);
        status = h2_conn_io_flush(&session->io);
        if (status != APR_SUCCESS) {
            return h2_session_status_from_apr_status(status);
        }
        return NGHTTP2_ERR_WOULDBLOCK;
    }
    
    status = h2_conn_io_write(&session->io, (const char *)data, length);
    if (status == APR_SUCCESS) {
        return length;
@@ -576,6 +589,17 @@ static int on_send_data_cb(nghttp2_session *ngh2,
    
    (void)ngh2;
    (void)source;
    if (h2_conn_io_needs_flush(&session->io)) {
        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
                      "h2_stream(%ld-%d): blocking due to io flush",
                      session->id, stream_id);
        status = h2_conn_io_flush(&session->io);
        if (status != APR_SUCCESS) {
            return h2_session_status_from_apr_status(status);
        }
        return NGHTTP2_ERR_WOULDBLOCK;
    }
    
    if (frame->data.padlen > H2_MAX_PADLEN) {
        return NGHTTP2_ERR_PROTO;
    }
@@ -1402,7 +1426,7 @@ static apr_status_t h2_session_send(h2_session *session)
        apr_socket_timeout_set(socket, saved_timeout);
    }
    session->have_written = 1;
    if (rv != 0) {
    if (rv != 0 && rv != NGHTTP2_ERR_WOULDBLOCK) {
        if (nghttp2_is_fatal(rv)) {
            dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, rv, nghttp2_strerror(rv));
            return APR_EGENERAL;
@@ -2028,6 +2052,21 @@ static void dispatch_event(h2_session *session, h2_session_event_t ev,
    }
}

/* trigger window updates, stream resumes and submits */
static apr_status_t dispatch_master(h2_session *session) {
    apr_status_t status;
    
    status = h2_mplx_dispatch_master_events(session->mplx, 
                                            on_stream_resume, session);
    if (status != APR_SUCCESS) {
        ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, session->c,
                      "h2_session(%ld): dispatch error", session->id);
        dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 
                       H2_ERR_INTERNAL_ERROR, "dispatch error");
    }
    return status;
}

static const int MAX_WAIT_MICROS = 200 * 1000;

apr_status_t h2_session_process(h2_session *session, int async)
@@ -2204,17 +2243,8 @@ apr_status_t h2_session_process(h2_session *session, int async)
                    }
                }

                /* trigger window updates, stream resumes and submits */
                status = h2_mplx_dispatch_master_events(session->mplx, 
                                                        on_stream_resume,
                                                        session);
                status = dispatch_master(session);
                if (status != APR_SUCCESS) {
                    ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, c,
                                  "h2_session(%ld): dispatch error", 
                                  session->id);
                    dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 
                                   H2_ERR_INTERNAL_ERROR, 
                                   "dispatch error");
                    break;
                }
                
+2 −2
Original line number Diff line number Diff line
@@ -102,9 +102,9 @@ typedef struct h2_session {
    
    struct h2_push_diary *push_diary; /* remember pushes, avoid duplicates */
    
    int open_streams;               /* number of streams open */
    int open_streams;               /* number of client streams open */
    int unsent_submits;             /* number of submitted, but not yet written responses. */
    int unsent_promises;            /* number of submitted, but not yet written push promised */
    int unsent_promises;            /* number of submitted, but not yet written push promises */
                                         
    int responses_submitted;        /* number of http/2 responses submitted */
    int streams_reset;              /* number of http/2 streams reset by client */
Loading