1
0
mirror of https://github.com/apache/httpd.git synced 2025-08-08 15:02:10 +03:00

mod_http2: some DoS protection, fix for read after free

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1733113 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stefan Eissing
2016-03-01 17:19:25 +00:00
parent 66c700aa62
commit c828bbc586
12 changed files with 294 additions and 42 deletions

12
CHANGES
View File

@@ -1,6 +1,18 @@
-*- coding: utf-8 -*- -*- coding: utf-8 -*-
Changes with Apache 2.5.0 Changes with Apache 2.5.0
*) mod_http2: Fixed possible read after free when streams were cancelled early
by the client.
Fixed apr_uint64_t formatting in a log statement to user proper APR def.
Number of worker threads allowed to a connection is adjusting dynamically.
Starting with 4, the number is doubled when streams can be served without
the server ever having to wait on the client. The number is halfed, when
the server has to wait on flow control grants. This can happen with a
maximum frequency of 5 times per second. When a connection occupies too
many workers, repeatable requests (GET/HEAD/OPTIONS) are cancelled and
placed back in the queue. Should that not suffice and a stream is busy
longer than the server timeout, the connection will be aborted.
*) mod_ssl: Fix a possible memory leak on restart for custom [EC]DH params. *) mod_ssl: Fix a possible memory leak on restart for custom [EC]DH params.
[Jan Kaluza, Yann Ylavic] [Jan Kaluza, Yann Ylavic]

View File

@@ -33,17 +33,47 @@
#include "h2_task.h" #include "h2_task.h"
#include "h2_util.h" #include "h2_util.h"
h2_io *h2_io_create(int id, apr_pool_t *pool) h2_io *h2_io_create(int id, apr_pool_t *pool, const h2_request *request)
{ {
h2_io *io = apr_pcalloc(pool, sizeof(*io)); h2_io *io = apr_pcalloc(pool, sizeof(*io));
if (io) { if (io) {
io->id = id; io->id = id;
io->pool = pool; io->pool = pool;
io->bucket_alloc = apr_bucket_alloc_create(pool); io->bucket_alloc = apr_bucket_alloc_create(pool);
io->request = h2_request_clone(pool, request);
} }
return io; return io;
} }
void h2_io_redo(h2_io *io)
{
io->worker_started = 0;
io->response = NULL;
io->rst_error = 0;
if (io->bbin) {
apr_brigade_cleanup(io->bbin);
}
if (io->bbout) {
apr_brigade_cleanup(io->bbout);
}
if (io->tmp) {
apr_brigade_cleanup(io->tmp);
}
io->started_at = io->done_at = 0;
}
int h2_io_is_repeatable(h2_io *io) {
if (io->submitted
|| io->input_consumed > 0
|| !io->request) {
/* cannot repeat that. */
return 0;
}
return (!strcmp("GET", io->request->method)
|| !strcmp("HEAD", io->request->method)
|| !strcmp("OPTIONS", io->request->method));
}
void h2_io_set_response(h2_io *io, h2_response *response) void h2_io_set_response(h2_io *io, h2_response *response)
{ {
AP_DEBUG_ASSERT(io->pool); AP_DEBUG_ASSERT(io->pool);

View File

@@ -49,8 +49,9 @@ struct h2_io {
apr_bucket_brigade *tmp; /* temporary data for chunking */ apr_bucket_brigade *tmp; /* temporary data for chunking */
unsigned int orphaned : 1; /* h2_stream is gone for this io */ unsigned int orphaned : 1; /* h2_stream is gone for this io */
unsigned int processing_started : 1; /* h2_worker started processing for this io */ unsigned int worker_started : 1; /* h2_worker started processing for this io */
unsigned int processing_done: 1; /* h2_worker finished for this io */ unsigned int worker_done : 1; /* h2_worker finished for this io */
unsigned int submitted : 1; /* response has been submitted to client */
unsigned int request_body : 1; /* iff request has body */ unsigned int request_body : 1; /* iff request has body */
unsigned int eos_in : 1; /* input eos has been seen */ unsigned int eos_in : 1; /* input eos has been seen */
unsigned int eos_in_written : 1; /* input eos has been forwarded */ unsigned int eos_in_written : 1; /* input eos has been forwarded */
@@ -61,6 +62,8 @@ struct h2_io {
struct apr_thread_cond_t *timed_cond; /* condition to wait on, maybe NULL */ struct apr_thread_cond_t *timed_cond; /* condition to wait on, maybe NULL */
apr_time_t timeout_at; /* when IO wait will time out */ apr_time_t timeout_at; /* when IO wait will time out */
apr_time_t started_at; /* when processing started */
apr_time_t done_at; /* when processing was done */
apr_size_t input_consumed; /* how many bytes have been read */ apr_size_t input_consumed; /* how many bytes have been read */
int files_handles_owned; int files_handles_owned;
@@ -73,7 +76,7 @@ struct h2_io {
/** /**
* Creates a new h2_io for the given stream id. * Creates a new h2_io for the given stream id.
*/ */
h2_io *h2_io_create(int id, apr_pool_t *pool); h2_io *h2_io_create(int id, apr_pool_t *pool, const struct h2_request *request);
/** /**
* Set the response of this stream. * Set the response of this stream.
@@ -85,6 +88,9 @@ void h2_io_set_response(h2_io *io, struct h2_response *response);
*/ */
void h2_io_rst(h2_io *io, int error); void h2_io_rst(h2_io *io, int error);
int h2_io_is_repeatable(h2_io *io);
void h2_io_redo(h2_io *io);
/** /**
* The input data is completely queued. Blocked reads will return immediately * The input data is completely queued. Blocked reads will return immediately
* and give either data or EOF. * and give either data or EOF.

View File

@@ -209,7 +209,11 @@ h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent,
m->stream_max_mem = h2_config_geti(conf, H2_CONF_STREAM_MAX_MEM); m->stream_max_mem = h2_config_geti(conf, H2_CONF_STREAM_MAX_MEM);
m->stream_timeout = stream_timeout; m->stream_timeout = stream_timeout;
m->workers = workers; m->workers = workers;
m->workers_max = 6; m->workers_max = h2_config_geti(conf, H2_CONF_MAX_WORKERS);
m->workers_def_limit = 4;
m->workers_limit = m->workers_def_limit;
m->last_limit_change = m->last_idle_block = apr_time_now();
m->limit_change_interval = apr_time_from_msec(200);
m->tx_handles_reserved = 0; m->tx_handles_reserved = 0;
m->tx_chunk_size = 4; m->tx_chunk_size = 4;
@@ -276,6 +280,9 @@ static void io_destroy(h2_mplx *m, h2_io *io, int events)
h2_io_set_remove(m->stream_ios, io); h2_io_set_remove(m->stream_ios, io);
h2_io_set_remove(m->ready_ios, io); h2_io_set_remove(m->ready_ios, io);
if (m->redo_ios) {
h2_io_set_remove(m->redo_ios, io);
}
if (pool) { if (pool) {
apr_pool_clear(pool); apr_pool_clear(pool);
@@ -292,7 +299,7 @@ static int io_stream_done(h2_mplx *m, h2_io *io, int rst_error)
{ {
/* Remove io from ready set, we will never submit it */ /* Remove io from ready set, we will never submit it */
h2_io_set_remove(m->ready_ios, io); h2_io_set_remove(m->ready_ios, io);
if (!io->processing_started || io->processing_done) { if (!io->worker_started || io->worker_done) {
/* already finished or not even started yet */ /* already finished or not even started yet */
h2_iq_remove(m->q, io->id); h2_iq_remove(m->q, io->id);
io_destroy(m, io, 1); io_destroy(m, io, 1);
@@ -321,7 +328,7 @@ static int stream_print(void *ctx, h2_io *io)
io->request->method, io->request->authority, io->request->path, io->request->method, io->request->authority, io->request->path,
io->response? "http" : (io->rst_error? "reset" : "?"), io->response? "http" : (io->rst_error? "reset" : "?"),
io->response? io->response->http_status : io->rst_error, io->response? io->response->http_status : io->rst_error,
io->orphaned, io->processing_started, io->processing_done, io->orphaned, io->worker_started, io->worker_done,
io->eos_in, io->eos_out); io->eos_in, io->eos_out);
} }
else if (io) { else if (io) {
@@ -331,7 +338,7 @@ static int stream_print(void *ctx, h2_io *io)
m->id, io->id, m->id, io->id,
io->response? "http" : (io->rst_error? "reset" : "?"), io->response? "http" : (io->rst_error? "reset" : "?"),
io->response? io->response->http_status : io->rst_error, io->response? io->response->http_status : io->rst_error,
io->orphaned, io->processing_started, io->processing_done, io->orphaned, io->worker_started, io->worker_done,
io->eos_in, io->eos_out); io->eos_in, io->eos_out);
} }
else { else {
@@ -647,6 +654,7 @@ h2_stream *h2_mplx_next_submit(h2_mplx *m, h2_ihash_t *streams)
if (io && !m->aborted) { if (io && !m->aborted) {
stream = h2_ihash_get(streams, io->id); stream = h2_ihash_get(streams, io->id);
if (stream) { if (stream) {
io->submitted = 1;
if (io->rst_error) { if (io->rst_error) {
h2_stream_rst(stream, io->rst_error); h2_stream_rst(stream, io->rst_error);
} }
@@ -667,7 +675,7 @@ h2_stream *h2_mplx_next_submit(h2_mplx *m, h2_ihash_t *streams)
"resetting io to close request processing", "resetting io to close request processing",
m->id, io->id); m->id, io->id);
h2_io_make_orphaned(io, H2_ERR_STREAM_CLOSED); h2_io_make_orphaned(io, H2_ERR_STREAM_CLOSED);
if (!io->processing_started || io->processing_done) { if (!io->worker_started || io->worker_done) {
io_destroy(m, io, 1); io_destroy(m, io, 1);
} }
else { else {
@@ -989,7 +997,7 @@ apr_status_t h2_mplx_reprioritize(h2_mplx *m, h2_stream_pri_cmp *cmp, void *ctx)
return status; return status;
} }
static h2_io *open_io(h2_mplx *m, int stream_id) static h2_io *open_io(h2_mplx *m, int stream_id, const h2_request *request)
{ {
apr_pool_t *io_pool = m->spare_pool; apr_pool_t *io_pool = m->spare_pool;
h2_io *io; h2_io *io;
@@ -1002,7 +1010,7 @@ static h2_io *open_io(h2_mplx *m, int stream_id)
m->spare_pool = NULL; m->spare_pool = NULL;
} }
io = h2_io_create(stream_id, io_pool); io = h2_io_create(stream_id, io_pool, request);
h2_io_set_add(m->stream_ios, io); h2_io_set_add(m->stream_ios, io);
return io; return io;
@@ -1022,8 +1030,7 @@ apr_status_t h2_mplx_process(h2_mplx *m, int stream_id, const h2_request *req,
status = APR_ECONNABORTED; status = APR_ECONNABORTED;
} }
else { else {
h2_io *io = open_io(m, stream_id); h2_io *io = open_io(m, stream_id, req);
io->request = req;
if (!io->request->body) { if (!io->request->body) {
status = h2_io_in_close(io); status = h2_io_in_close(io);
@@ -1050,15 +1057,21 @@ static h2_task *pop_task(h2_mplx *m)
h2_task *task = NULL; h2_task *task = NULL;
int sid; int sid;
while (!m->aborted && !task while (!m->aborted && !task
&& (m->workers_busy < m->workers_max) && (m->workers_busy < m->workers_limit)
&& (sid = h2_iq_shift(m->q)) > 0) { && (sid = h2_iq_shift(m->q)) > 0) {
h2_io *io = h2_io_set_get(m->stream_ios, sid); h2_io *io = h2_io_set_get(m->stream_ios, sid);
if (io) { if (io && io->orphaned) {
io_destroy(m, io, 0);
if (m->join_wait) {
apr_thread_cond_signal(m->join_wait);
}
}
else if (io) {
conn_rec *slave = h2_slave_create(m->c, m->pool, m->spare_allocator); conn_rec *slave = h2_slave_create(m->c, m->pool, m->spare_allocator);
m->spare_allocator = NULL; m->spare_allocator = NULL;
task = h2_task_create(m->id, io->request, slave, m); task = h2_task_create(m->id, io->request, slave, m);
io->worker_started = 1;
io->processing_started = 1; io->started_at = apr_time_now();
if (sid > m->max_stream_started) { if (sid > m->max_stream_started) {
m->max_stream_started = sid; m->max_stream_started = sid;
} }
@@ -1102,6 +1115,7 @@ static void task_done(h2_mplx *m, h2_task *task)
} }
else { else {
h2_io *io = h2_io_set_get(m->stream_ios, task->stream_id); h2_io *io = h2_io_set_get(m->stream_ios, task->stream_id);
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c,
"h2_mplx(%ld): task(%s) done", m->id, task->id); "h2_mplx(%ld): task(%s) done", m->id, task->id);
/* clean our references and report request as done. Signal /* clean our references and report request as done. Signal
@@ -1117,7 +1131,39 @@ static void task_done(h2_mplx *m, h2_task *task)
h2_slave_destroy(task->c, &m->spare_allocator); h2_slave_destroy(task->c, &m->spare_allocator);
task = NULL; task = NULL;
if (io) { if (io) {
io->processing_done = 1; apr_time_t now = apr_time_now();
if (!io->orphaned && m->redo_ios
&& h2_io_set_get(m->redo_ios, io->id)) {
/* reset and schedule again */
h2_io_redo(io);
h2_io_set_remove(m->redo_ios, io);
h2_iq_add(m->q, io->id, NULL, NULL);
}
else {
io->worker_done = 1;
io->done_at = now;
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c,
"h2_mplx(%ld): request(%d) done, %f ms"
" elapsed", m->id, io->id,
(io->done_at - io->started_at) / 1000.0);
if (io->started_at > m->last_idle_block) {
/* this task finished without causing an 'idle block', e.g.
* a block by flow control.
*/
if (now - m->last_limit_change >= m->limit_change_interval
&& m->workers_limit < m->workers_max) {
/* Well behaving stream, allow it more workers */
m->workers_limit = H2MIN(m->workers_limit * 2,
m->workers_max);
m->last_limit_change = now;
m->need_registration = 1;
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c,
"h2_mplx(%ld): increase worker limit to %d",
m->id, m->workers_limit);
}
}
}
if (io->orphaned) { if (io->orphaned) {
io_destroy(m, io, 0); io_destroy(m, io, 0);
if (m->join_wait) { if (m->join_wait) {
@@ -1131,12 +1177,11 @@ static void task_done(h2_mplx *m, h2_task *task)
apr_thread_cond_broadcast(m->task_done); apr_thread_cond_broadcast(m->task_done);
} }
} }
} }
void h2_mplx_task_done(h2_mplx *m, h2_task *task, h2_task **ptask) void h2_mplx_task_done(h2_mplx *m, h2_task *task, h2_task **ptask)
{ {
int acquired, do_registration = 0; int acquired;
if (enter_mutex(m, &acquired) == APR_SUCCESS) { if (enter_mutex(m, &acquired) == APR_SUCCESS) {
task_done(m, task); task_done(m, task);
@@ -1145,12 +1190,147 @@ void h2_mplx_task_done(h2_mplx *m, h2_task *task, h2_task **ptask)
/* caller wants another task */ /* caller wants another task */
*ptask = pop_task(m); *ptask = pop_task(m);
} }
do_registration = (m->workers_busy+1 == m->workers_max);
leave_mutex(m, acquired); leave_mutex(m, acquired);
} }
if (do_registration) { }
workers_register(m);
/*******************************************************************************
* h2_mplx DoS protection
******************************************************************************/
typedef struct {
h2_mplx *m;
h2_io *io;
apr_time_t now;
} io_iter_ctx;
static int latest_repeatable_busy_unsubmitted_iter(void *data, h2_io *io)
{
io_iter_ctx *ctx = data;
if (io->worker_started && !io->worker_done
&& h2_io_is_repeatable(io)
&& !h2_io_set_get(ctx->m->redo_ios, io->id)) {
/* this io occupies a worker, the response has not been submitted yet,
* not been cancelled and it is a repeatable request
* -> it can be re-scheduled later */
if (!ctx->io || ctx->io->started_at < io->started_at) {
/* we did not have one or this one was started later */
ctx->io = io;
}
} }
return 1;
}
static h2_io *get_latest_repeatable_busy_unsubmitted_io(h2_mplx *m)
{
io_iter_ctx ctx;
ctx.m = m;
ctx.io = NULL;
h2_io_set_iter(m->stream_ios, latest_repeatable_busy_unsubmitted_iter, &ctx);
return ctx.io;
}
static int timed_out_busy_iter(void *data, h2_io *io)
{
io_iter_ctx *ctx = data;
if (io->worker_started && !io->worker_done
&& (ctx->now - io->started_at) > ctx->m->stream_timeout) {
/* timed out stream occupying a worker, found */
ctx->io = io;
return 0;
}
return 1;
}
static h2_io *get_timed_out_busy_stream(h2_mplx *m)
{
io_iter_ctx ctx;
ctx.m = m;
ctx.io = NULL;
ctx.now = apr_time_now();
h2_io_set_iter(m->stream_ios, timed_out_busy_iter, &ctx);
return ctx.io;
}
static apr_status_t unschedule_slow_ios(h2_mplx *m)
{
h2_io *io;
int n;
if (!m->redo_ios) {
m->redo_ios = h2_io_set_create(m->pool);
}
/* Try to get rid of streams that occupy workers. Look for safe requests
* that are repeatable. If none found, fail the connection.
*/
n = (m->workers_busy - m->workers_limit - h2_io_set_size(m->redo_ios));
while (n > 0 && (io = get_latest_repeatable_busy_unsubmitted_io(m))) {
h2_io_set_add(m->redo_ios, io);
h2_io_rst(io, H2_ERR_CANCEL);
--n;
}
if ((m->workers_busy - h2_io_set_size(m->redo_ios)) > m->workers_limit) {
io = get_timed_out_busy_stream(m);
if (io) {
/* Too many busy workers, unable to cancel enough streams
* and with a busy, timed out stream, we tell the client
* to go away... */
return APR_TIMEUP;
}
}
return APR_SUCCESS;
}
apr_status_t h2_mplx_idle(h2_mplx *m)
{
apr_status_t status = APR_SUCCESS;
apr_time_t now;
int acquired;
if (enter_mutex(m, &acquired) == APR_SUCCESS) {
apr_size_t scount = h2_io_set_size(m->stream_ios);
if (scount > 0 && m->workers_busy) {
/* If we have streams in connection state 'IDLE', meaning
* all streams are ready to sent data out, but lack
* WINDOW_UPDATEs.
*
* This is ok, unless we have streams that still occupy
* h2 workers. As worker threads are a scarce resource,
* we need to take measures that we do not get DoSed.
*
* This is what we call an 'idle block'. Limit the amount
* of busy workers we allow for this connection until it
* well behaves.
*/
now = apr_time_now();
m->last_idle_block = now;
if (m->workers_limit > 2
&& now - m->last_limit_change >= m->limit_change_interval) {
if (m->workers_limit > 16) {
m->workers_limit = 16;
}
else if (m->workers_limit > 8) {
m->workers_limit = 8;
}
else if (m->workers_limit > 4) {
m->workers_limit = 4;
}
else if (m->workers_limit > 2) {
m->workers_limit = 2;
}
m->last_limit_change = now;
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c,
"h2_mplx(%ld): decrease worker limit to %d",
m->id, m->workers_limit);
}
if (m->workers_busy > m->workers_limit) {
status = unschedule_slow_ios(m);
}
}
leave_mutex(m, acquired);
}
return status;
} }
/******************************************************************************* /*******************************************************************************

View File

@@ -68,15 +68,22 @@ struct h2_mplx {
apr_pool_t *pool; apr_pool_t *pool;
unsigned int aborted : 1; unsigned int aborted : 1;
unsigned int need_registration : 1;
struct h2_int_queue *q; struct h2_int_queue *q;
struct h2_io_set *stream_ios; struct h2_io_set *stream_ios;
struct h2_io_set *ready_ios; struct h2_io_set *ready_ios;
struct h2_io_set *redo_ios;
int max_stream_started; /* highest stream id that started processing */ int max_stream_started; /* highest stream id that started processing */
int workers_busy; /* # of workers processing on this mplx */ int workers_busy; /* # of workers processing on this mplx */
int workers_max; /* max # of workers occupied by this mplx */ int workers_limit; /* current # of workers limit, dynamic */
int need_registration; int workers_def_limit; /* default # of workers limit */
int workers_max; /* max, hard limit # of workers in a process */
apr_time_t last_idle_block; /* last time, this mplx entered IDLE while
* streams were ready */
apr_time_t last_limit_change;/* last time, worker limit changed */
apr_interval_time_t limit_change_interval;
apr_thread_mutex_t *lock; apr_thread_mutex_t *lock;
struct apr_thread_cond_t *added_output; struct apr_thread_cond_t *added_output;
@@ -389,6 +396,16 @@ APR_RING_INSERT_TAIL((b), ap__b, h2_mplx, link); \
*/ */
#define H2_MPLX_REMOVE(e) APR_RING_REMOVE((e), link) #define H2_MPLX_REMOVE(e) APR_RING_REMOVE((e), link)
/*******************************************************************************
* h2_mplx DoS protection
******************************************************************************/
/**
* Master connection has entered idle mode.
* @param m the mplx instance of the master connection
* @return != SUCCESS iff connection should be terminated
*/
apr_status_t h2_mplx_idle(h2_mplx *m);
/******************************************************************************* /*******************************************************************************
* h2_mplx h2_req_engine handling. * h2_mplx h2_req_engine handling.

View File

@@ -778,7 +778,7 @@ static apr_status_t gset_encode_next(gset_encoder *encoder, apr_uint64_t pval)
/* Intentional no APLOGNO */ /* Intentional no APLOGNO */
ap_log_perror(APLOG_MARK, GCSLOG_LEVEL, 0, encoder->pool, ap_log_perror(APLOG_MARK, GCSLOG_LEVEL, 0, encoder->pool,
"h2_push_diary_enc: val=%"APR_UINT64_T_HEX_FMT", delta=%" "h2_push_diary_enc: val=%"APR_UINT64_T_HEX_FMT", delta=%"
APR_UINT64_T_HEX_FMT" flex_bits=%" APR_UINT64_T_FMT APR_UINT64_T_HEX_FMT" flex_bits=%"APR_UINT64_T_FMT", "
", fixed_bits=%d, fixed_val=%"APR_UINT64_T_HEX_FMT, ", fixed_bits=%d, fixed_val=%"APR_UINT64_T_HEX_FMT,
pval, delta, flex_bits, encoder->fixed_bits, delta&encoder->fixed_mask); pval, delta, flex_bits, encoder->fixed_bits, delta&encoder->fixed_mask);
for (; flex_bits != 0; --flex_bits) { for (; flex_bits != 0; --flex_bits) {

View File

@@ -60,10 +60,6 @@ h2_request *h2_request_createn(int id, apr_pool_t *pool,
return req; return req;
} }
void h2_request_destroy(h2_request *req)
{
}
static apr_status_t inspect_clen(h2_request *req, const char *s) static apr_status_t inspect_clen(h2_request *req, const char *s)
{ {
char *end; char *end;
@@ -342,11 +338,22 @@ void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src)
dst->authority = OPT_COPY(p, src->authority); dst->authority = OPT_COPY(p, src->authority);
dst->path = OPT_COPY(p, src->path); dst->path = OPT_COPY(p, src->path);
dst->headers = apr_table_clone(p, src->headers); dst->headers = apr_table_clone(p, src->headers);
if (src->trailers) {
dst->trailers = apr_table_clone(p, src->trailers);
}
dst->content_length = src->content_length; dst->content_length = src->content_length;
dst->chunked = src->chunked; dst->chunked = src->chunked;
dst->eoh = src->eoh; dst->eoh = src->eoh;
} }
h2_request *h2_request_clone(apr_pool_t *p, const h2_request *src)
{
h2_request *nreq = apr_pcalloc(p, sizeof(*nreq));
memcpy(nreq, src, sizeof(*nreq));
h2_request_copy(p, nreq, src);
return nreq;
}
request_rec *h2_request_create_rec(const h2_request *req, conn_rec *conn) request_rec *h2_request_create_rec(const h2_request *req, conn_rec *conn)
{ {
request_rec *r; request_rec *r;

View File

@@ -30,8 +30,6 @@ apr_status_t h2_request_make(h2_request *req, apr_pool_t *pool,
const char *authority, const char *path, const char *authority, const char *path,
apr_table_t *headers); apr_table_t *headers);
void h2_request_destroy(h2_request *req);
apr_status_t h2_request_rwrite(h2_request *req, request_rec *r); apr_status_t h2_request_rwrite(h2_request *req, request_rec *r);
apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool, apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool,
@@ -47,6 +45,8 @@ apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool,
void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src); void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src);
h2_request *h2_request_clone(apr_pool_t *p, const h2_request *src);
/** /**
* Create a request_rec representing the h2_request to be * Create a request_rec representing the h2_request to be
* processed on the given connection. * processed on the given connection.

View File

@@ -2015,7 +2015,7 @@ apr_status_t h2_session_process(h2_session *session, int async)
no_streams = h2_ihash_is_empty(session->streams); no_streams = h2_ihash_is_empty(session->streams);
update_child_status(session, (no_streams? SERVER_BUSY_KEEPALIVE update_child_status(session, (no_streams? SERVER_BUSY_KEEPALIVE
: SERVER_BUSY_READ), "idle"); : SERVER_BUSY_READ), "idle");
if (async && !session->r && session->requests_received && no_streams) { if (async && no_streams && !session->r && session->requests_received) {
ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c, ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
"h2_session(%ld): async idle, nonblock read", session->id); "h2_session(%ld): async idle, nonblock read", session->id);
/* We do not return to the async mpm immediately, since under /* We do not return to the async mpm immediately, since under
@@ -2051,7 +2051,13 @@ apr_status_t h2_session_process(h2_session *session, int async)
} }
else { else {
/* We wait in smaller increments, using a 1 second timeout. /* We wait in smaller increments, using a 1 second timeout.
* That gives us the chance to check for MPMQ_STOPPING often. */ * That gives us the chance to check for MPMQ_STOPPING often.
*/
status = h2_mplx_idle(session->mplx);
if (status != APR_SUCCESS) {
dispatch_event(session, H2_SESSION_EV_CONN_ERROR,
H2_ERR_ENHANCE_YOUR_CALM, "less is more");
}
h2_filter_cin_timeout_set(session->cin, apr_time_from_sec(1)); h2_filter_cin_timeout_set(session->cin, apr_time_from_sec(1));
status = h2_session_read(session, 1); status = h2_session_read(session, 1);
if (status == APR_SUCCESS) { if (status == APR_SUCCESS) {

View File

@@ -169,11 +169,6 @@ h2_stream *h2_stream_open(int id, apr_pool_t *pool, h2_session *session)
apr_status_t h2_stream_destroy(h2_stream *stream) apr_status_t h2_stream_destroy(h2_stream *stream)
{ {
AP_DEBUG_ASSERT(stream); AP_DEBUG_ASSERT(stream);
if (stream->request) {
h2_request_destroy(stream->request);
stream->request = NULL;
}
if (stream->pool) { if (stream->pool) {
apr_pool_destroy(stream->pool); apr_pool_destroy(stream->pool);
} }

View File

@@ -26,7 +26,7 @@
* @macro * @macro
* Version number of the http2 module as c string * Version number of the http2 module as c string
*/ */
#define MOD_HTTP2_VERSION "1.3.1-DEV" #define MOD_HTTP2_VERSION "1.3.2-DEV"
/** /**
* @macro * @macro
@@ -34,7 +34,7 @@
* release. This is a 24 bit number with 8 bits for major number, 8 bits * release. This is a 24 bit number with 8 bits for major number, 8 bits
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
*/ */
#define MOD_HTTP2_VERSION_NUM 0x010301 #define MOD_HTTP2_VERSION_NUM 0x010302
#endif /* mod_h2_h2_version_h */ #endif /* mod_h2_h2_version_h */

View File

@@ -86,7 +86,6 @@ static h2_task *next_task(h2_workers *workers)
--workers->mplx_count; --workers->mplx_count;
task = h2_mplx_pop_task(m, &has_more); task = h2_mplx_pop_task(m, &has_more);
if (has_more) { if (has_more) {
H2_MPLX_LIST_INSERT_TAIL(&workers->mplxs, m); H2_MPLX_LIST_INSERT_TAIL(&workers->mplxs, m);
++workers->mplx_count; ++workers->mplx_count;