From 3f9e81826904dfc9e312bf529f90d2dffd0a3be9 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 29 Mar 2016 13:28:18 +0000 Subject: [PATCH] mod_http2: more effient passing of response bodies git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1737006 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 6 +- modules/http2/h2_io.c | 65 ++++++------------ modules/http2/h2_io.h | 10 +-- modules/http2/h2_mplx.c | 45 ++----------- modules/http2/h2_mplx.h | 16 +---- modules/http2/h2_session.c | 13 ++-- modules/http2/h2_stream.c | 132 +++++++++++-------------------------- modules/http2/h2_util.c | 88 ++++++++++++++++++++++++- modules/http2/h2_util.h | 34 ++++++++-- modules/http2/h2_version.h | 4 +- 10 files changed, 197 insertions(+), 216 deletions(-) diff --git a/CHANGES b/CHANGES index ec9f9d44f6..526b318f20 100644 --- a/CHANGES +++ b/CHANGES @@ -1,11 +1,9 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.0 - *) mod_http2: fix for missing score board updates on request count, fix for - memory leak on slave connection reuse. + *) mod_http2: more efficient passing of response bodies with less contention + and file bucket forwarding. [Stefan Eissing] - *) mod_http2: disabling PUSH when client sends GOAWAY. - *) mod_proxy_http2: using HTTP/2 flow control for backend streams by observing data actually send out on the frontend h2 connection. [Stefan Eissing] diff --git a/modules/http2/h2_io.c b/modules/http2/h2_io.c index 3b36ca181e..64cb8b0178 100644 --- a/modules/http2/h2_io.c +++ b/modules/http2/h2_io.c @@ -346,63 +346,36 @@ apr_status_t h2_io_in_close(h2_io *io) return APR_SUCCESS; } -static int is_out_readable(h2_io *io, apr_off_t *plen, int *peos, - apr_status_t *ps) +apr_status_t h2_io_out_get_brigade(h2_io *io, apr_bucket_brigade *bb, + apr_off_t len) { if (io->rst_error) { - *ps = APR_ECONNABORTED; - return 0; + return APR_ECONNABORTED; } if (io->eos_out_read) { - *plen = 0; - *peos = 1; - *ps = APR_SUCCESS; - return 0; + return APR_EOF; } else if (!io->bbout) { - *plen = 0; - *peos = 0; - *ps = APR_EAGAIN; - return 0; - } - return 1; -} - -apr_status_t h2_io_out_readx(h2_io *io, - h2_io_data_cb *cb, void *ctx, - apr_off_t *plen, int *peos) -{ - apr_status_t status; - if (!is_out_readable(io, plen, peos, &status)) { - return status; - } - if (cb == NULL) { - /* just checking length available */ - status = h2_util_bb_avail(io->bbout, plen, peos); + return APR_EAGAIN; } else { - status = h2_util_bb_readx(io->bbout, cb, ctx, plen, peos); - if (status == APR_SUCCESS) { - io->eos_out_read = *peos; - io->output_consumed += *plen; + apr_status_t status; + apr_off_t pre_len, post_len; + /* Allow file handles pass through without limits. If they + * already have the lifetime of this stream, we might as well + * pass them on to the master connection */ + apr_size_t files = INT_MAX; + + apr_brigade_length(bb, 0, &pre_len); + status = h2_util_move(bb, io->bbout, len, &files, "h2_io_read_to"); + if (status == APR_SUCCESS && io->eos_out + && APR_BRIGADE_EMPTY(io->bbout)) { + io->eos_out_read = 1; } - } - return status; -} - -apr_status_t h2_io_out_read_to(h2_io *io, apr_bucket_brigade *bb, - apr_off_t *plen, int *peos) -{ - apr_status_t status; - if (!is_out_readable(io, plen, peos, &status)) { + apr_brigade_length(bb, 0, &post_len); + io->output_consumed += (post_len - pre_len); return status; } - status = h2_util_move(bb, io->bbout, *plen, NULL, "h2_io_read_to"); - if (status == APR_SUCCESS && io->eos_out && APR_BRIGADE_EMPTY(io->bbout)) { - io->eos_out_read = *peos = 1; - } - io->output_consumed += *plen; - return status; } static void process_trailers(h2_io *io, apr_table_t *trailers) diff --git a/modules/http2/h2_io.h b/modules/http2/h2_io.h index 90d0cde8f2..7730d7754e 100644 --- a/modules/http2/h2_io.h +++ b/modules/http2/h2_io.h @@ -151,13 +151,9 @@ apr_status_t h2_io_in_shutdown(h2_io *io); * @param plen the requested max len, set to amount of data on return * @param peos != 0 iff the end of stream has been reached */ -apr_status_t h2_io_out_readx(h2_io *io, - h2_io_data_cb *cb, void *ctx, - apr_off_t *plen, int *peos); - -apr_status_t h2_io_out_read_to(h2_io *io, - apr_bucket_brigade *bb, - apr_off_t *plen, int *peos); +apr_status_t h2_io_out_get_brigade(h2_io *io, + apr_bucket_brigade *bb, + apr_off_t len); apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, apr_size_t maxlen, apr_table_t *trailers, diff --git a/modules/http2/h2_mplx.c b/modules/http2/h2_mplx.c index 45326bd0f4..6be9053969 100644 --- a/modules/http2/h2_mplx.c +++ b/modules/http2/h2_mplx.c @@ -613,40 +613,9 @@ apr_status_t h2_mplx_in_update_windows(h2_mplx *m) return status; } -apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id, - h2_io_data_cb *cb, void *ctx, - apr_off_t *plen, int *peos, - apr_table_t **ptrailers) -{ - apr_status_t status; - int acquired; - - AP_DEBUG_ASSERT(m); - if ((status = enter_mutex(m, &acquired)) == APR_SUCCESS) { - h2_io *io = h2_io_set_get(m->stream_ios, stream_id); - if (io && !io->orphaned) { - H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_pre"); - - status = h2_io_out_readx(io, cb, ctx, plen, peos); - H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_post"); - if (status == APR_SUCCESS && cb) { - h2_io_signal(io, H2_IO_WRITE); - } - } - else { - status = APR_ECONNABORTED; - } - - *ptrailers = (*peos && io->response)? io->response->trailers : NULL; - leave_mutex(m, acquired); - } - return status; -} - -apr_status_t h2_mplx_out_read_to(h2_mplx *m, int stream_id, - apr_bucket_brigade *bb, - apr_off_t *plen, int *peos, - apr_table_t **ptrailers) +apr_status_t h2_mplx_out_get_brigade(h2_mplx *m, int stream_id, + apr_bucket_brigade *bb, + apr_off_t len, apr_table_t **ptrailers) { apr_status_t status; int acquired; @@ -655,11 +624,11 @@ apr_status_t h2_mplx_out_read_to(h2_mplx *m, int stream_id, if ((status = enter_mutex(m, &acquired)) == APR_SUCCESS) { h2_io *io = h2_io_set_get(m->stream_ios, stream_id); if (io && !io->orphaned) { - H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_read_to_pre"); + H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_get_brigade_pre"); - status = h2_io_out_read_to(io, bb, plen, peos); + status = h2_io_out_get_brigade(io, bb, len); - H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_read_to_post"); + H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_get_brigade_post"); if (status == APR_SUCCESS) { h2_io_signal(io, H2_IO_WRITE); } @@ -667,7 +636,7 @@ apr_status_t h2_mplx_out_read_to(h2_mplx *m, int stream_id, else { status = APR_ECONNABORTED; } - *ptrailers = (*peos && io->response)? io->response->trailers : NULL; + *ptrailers = io->response? io->response->trailers : NULL; leave_mutex(m, acquired); } return status; diff --git a/modules/http2/h2_mplx.h b/modules/http2/h2_mplx.h index dac5aac4c0..aa1a04f877 100644 --- a/modules/http2/h2_mplx.h +++ b/modules/http2/h2_mplx.h @@ -265,23 +265,13 @@ apr_status_t h2_mplx_in_update_windows(h2_mplx *m); struct h2_stream *h2_mplx_next_submit(h2_mplx *m, struct h2_ihash_t *streams); -/** - * Reads output data from the given stream. Will never block, but - * return APR_EAGAIN until data arrives or the stream is closed. - */ -apr_status_t h2_mplx_out_readx(h2_mplx *mplx, int stream_id, - h2_io_data_cb *cb, void *ctx, - apr_off_t *plen, int *peos, - apr_table_t **ptrailers); - /** * Reads output data into the given brigade. Will never block, but * return APR_EAGAIN until data arrives or the stream is closed. */ -apr_status_t h2_mplx_out_read_to(h2_mplx *mplx, int stream_id, - apr_bucket_brigade *bb, - apr_off_t *plen, int *peos, - apr_table_t **ptrailers); +apr_status_t h2_mplx_out_get_brigade(h2_mplx *mplx, int stream_id, + apr_bucket_brigade *bb, + apr_off_t len, apr_table_t **ptrailers); /** * Opens the output for the given stream with the specified response. diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c index 27f1de8294..8111d93b66 100644 --- a/modules/http2/h2_session.c +++ b/modules/http2/h2_session.c @@ -555,6 +555,7 @@ static int on_send_data_cb(nghttp2_session *ngh2, unsigned char padlen; int eos; h2_stream *stream; + apr_bucket *b; (void)ngh2; (void)source; @@ -599,16 +600,10 @@ static int on_send_data_cb(nghttp2_session *ngh2, } } else { - apr_bucket *b; - char *header = apr_pcalloc(stream->pool, 10); - memcpy(header, (const char *)framehd, 9); - if (padlen) { - header[9] = (char)padlen; + status = h2_conn_io_write(&session->io, (const char *)framehd, 9); + if (padlen && status == APR_SUCCESS) { + status = h2_conn_io_write(&session->io, (const char *)&padlen, 1); } - b = apr_bucket_pool_create(header, padlen? 10 : 9, - stream->pool, session->c->bucket_alloc); - status = h2_conn_io_writeb(&session->io, b); - if (status == APR_SUCCESS) { apr_off_t len = length; status = h2_stream_read_to(stream, session->io.output, &len, &eos); diff --git a/modules/http2/h2_stream.c b/modules/http2/h2_stream.c index 2b368b67cf..fc41fe352d 100644 --- a/modules/http2/h2_stream.c +++ b/modules/http2/h2_stream.c @@ -493,7 +493,9 @@ const h2_priority *h2_stream_get_priority(h2_stream *stream) typedef struct h2_sos_mplx { h2_mplx *m; apr_bucket_brigade *bb; + apr_bucket_brigade *tmp; apr_table_t *trailers; + apr_off_t buffer_size; } h2_sos_mplx; #define H2_SOS_MPLX_OUT(lvl,msos,msg) \ @@ -503,129 +505,79 @@ typedef struct h2_sos_mplx { } while(0) -static apr_status_t h2_sos_mplx_read_to(h2_sos *sos, apr_bucket_brigade *bb, - apr_off_t *plen, int *peos) +static apr_status_t mplx_transfer(h2_sos_mplx *msos, int stream_id, + apr_pool_t *pool) { - h2_sos_mplx *msos = sos->ctx; - apr_status_t status = APR_SUCCESS; + apr_status_t status; apr_table_t *trailers = NULL; - - H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx read_to_pre"); - if (APR_BRIGADE_EMPTY(msos->bb)) { - apr_off_t tlen = *plen; - int eos; - status = h2_mplx_out_read_to(msos->m, sos->stream->id, - msos->bb, &tlen, &eos, &trailers); + if (!msos->tmp) { + msos->tmp = apr_brigade_create(msos->bb->p, msos->bb->bucket_alloc); } - - if (status == APR_SUCCESS && !APR_BRIGADE_EMPTY(msos->bb)) { - status = h2_transfer_brigade(bb, msos->bb, sos->stream->pool, - plen, peos); - } - else { - *plen = 0; - *peos = 0; + status = h2_mplx_out_get_brigade(msos->m, stream_id, msos->tmp, + msos->buffer_size-1, &trailers); + if (status == APR_SUCCESS) { + status = h2_transfer_brigade(msos->bb, msos->tmp, pool); } - if (trailers) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c, - "h2_stream(%ld-%d): read_to, saving trailers", - msos->m->id, sos->stream->id); msos->trailers = trailers; } - + return status; +} + +static apr_status_t h2_sos_mplx_read_to(h2_sos *sos, apr_bucket_brigade *bb, + apr_off_t *plen, int *peos) +{ + h2_sos_mplx *msos = sos->ctx; + apr_status_t status; + + status = h2_append_brigade(bb, msos->bb, plen, peos); if (status == APR_SUCCESS && !*peos && !*plen) { status = APR_EAGAIN; } - H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx read_to_post"); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c, "h2_stream(%ld-%d): read_to, len=%ld eos=%d", msos->m->id, sos->stream->id, (long)*plen, *peos); return status; } -static apr_status_t h2_sos_mplx_prep_read(h2_sos *sos, apr_off_t *plen, int *peos) +static apr_status_t h2_sos_mplx_readx(h2_sos *sos, h2_io_data_cb *cb, void *ctx, + apr_off_t *plen, int *peos) { h2_sos_mplx *msos = sos->ctx; apr_status_t status = APR_SUCCESS; - const char *src; - apr_table_t *trailers = NULL; - int test_read = (*plen == 0); - H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx prep_read_pre"); - if (!APR_BRIGADE_EMPTY(msos->bb)) { - src = "stream"; - status = h2_util_bb_avail(msos->bb, plen, peos); - if (!test_read && status == APR_SUCCESS && !*peos && !*plen) { - apr_brigade_cleanup(msos->bb); - return h2_sos_mplx_prep_read(sos, plen, peos); - } - } - else { - src = "mplx"; - status = h2_mplx_out_readx(msos->m, sos->stream->id, - NULL, NULL, plen, peos, &trailers); - if (trailers) { - msos->trailers = trailers; - } - } - - if (!test_read && status == APR_SUCCESS && !*peos && !*plen) { + status = h2_util_bb_readx(msos->bb, cb, ctx, plen, peos); + if (status == APR_SUCCESS && !*peos && !*plen) { status = APR_EAGAIN; } - - H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx prep_read_post"); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c, - "h2_stream(%ld-%d): prep_read %s, len=%ld eos=%d, trailers=%s", - msos->m->id, sos->stream->id, src, (long)*plen, *peos, - msos->trailers? "yes" : "no"); + "h2_stream(%ld-%d): readx, len=%ld eos=%d", + msos->m->id, sos->stream->id, (long)*plen, *peos); return status; } -static apr_status_t h2_sos_mplx_readx(h2_sos *sos, h2_io_data_cb *cb, void *ctx, - apr_off_t *plen, int *peos) +static apr_status_t h2_sos_mplx_prep_read(h2_sos *sos, apr_off_t *plen, int *peos) { h2_sos_mplx *msos = sos->ctx; apr_status_t status = APR_SUCCESS; - apr_table_t *trailers = NULL; - const char *src; - H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx readx_pre"); - *peos = 0; - if (!APR_BRIGADE_EMPTY(msos->bb)) { - apr_off_t origlen = *plen; - - src = "stream"; - status = h2_util_bb_readx(msos->bb, cb, ctx, plen, peos); - if (status == APR_SUCCESS && !*peos && !*plen) { - apr_brigade_cleanup(msos->bb); - *plen = origlen; - return h2_sos_mplx_readx(sos, cb, ctx, plen, peos); - } - } - else { - src = "mplx"; - status = h2_mplx_out_readx(msos->m, sos->stream->id, - cb, ctx, plen, peos, &trailers); - } + H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx prep_read_pre"); - if (trailers) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c, - "h2_stream(%ld-%d): readx, saving trailers", - msos->m->id, sos->stream->id); - msos->trailers = trailers; + if (APR_BRIGADE_EMPTY(msos->bb)) { + status = mplx_transfer(msos, sos->stream->id, sos->stream->pool); } + status = h2_util_bb_avail(msos->bb, plen, peos); + H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx prep_read_post"); + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c, + "h2_stream(%ld-%d): prep_read, len=%ld eos=%d, trailers=%s", + msos->m->id, sos->stream->id, (long)*plen, *peos, + msos->trailers? "yes" : "no"); if (status == APR_SUCCESS && !*peos && !*plen) { status = APR_EAGAIN; } - H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_stream readx_post"); - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c, - "h2_stream(%ld-%d): readx %s, len=%ld eos=%d", - msos->m->id, sos->stream->id, src, (long)*plen, *peos); - return status; } @@ -642,13 +594,8 @@ static apr_status_t h2_sos_mplx_buffer(h2_sos *sos, apr_bucket_brigade *bb) apr_status_t status = APR_SUCCESS; if (bb && !APR_BRIGADE_EMPTY(bb)) { - apr_size_t move_all = INT_MAX; - /* we can move file handles from h2_mplx into this h2_stream as many - * as we want, since the lifetimes are the same and we are not freeing - * the ones in h2_mplx->io before this stream is done. */ H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx set_response_pre"); - status = h2_util_move(msos->bb, bb, 16 * 1024, &move_all, - "h2_stream_set_response"); + status = mplx_transfer(msos, sos->stream->id, sos->stream->pool); H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx set_response_post"); } return status; @@ -662,7 +609,8 @@ static h2_sos *h2_sos_mplx_create(h2_stream *stream, h2_response *response) msos = apr_pcalloc(stream->pool, sizeof(*msos)); msos->m = stream->session->mplx; msos->bb = apr_brigade_create(stream->pool, msos->m->c->bucket_alloc); - + msos->buffer_size = 32 * 1024; + sos = apr_pcalloc(stream->pool, sizeof(*sos)); sos->stream = stream; sos->response = response; diff --git a/modules/http2/h2_util.c b/modules/http2/h2_util.c index 94a2987ca0..002284a536 100644 --- a/modules/http2/h2_util.c +++ b/modules/http2/h2_util.c @@ -861,7 +861,7 @@ void h2_util_bb_log(conn_rec *c, int stream_id, int level, } -apr_status_t h2_transfer_brigade(apr_bucket_brigade *to, +apr_status_t h2_ltransfer_brigade(apr_bucket_brigade *to, apr_bucket_brigade *from, apr_pool_t *p, apr_off_t *plen, @@ -932,6 +932,92 @@ apr_status_t h2_transfer_brigade(apr_bucket_brigade *to, return APR_SUCCESS; } +apr_status_t h2_transfer_brigade(apr_bucket_brigade *to, + apr_bucket_brigade *from, + apr_pool_t *p) +{ + apr_bucket *e; + apr_status_t rv; + + while (!APR_BRIGADE_EMPTY(from)) { + e = APR_BRIGADE_FIRST(from); + + rv = apr_bucket_setaside(e, p); + + /* If the bucket type does not implement setaside, then + * (hopefully) morph it into a bucket type which does, and set + * *that* aside... */ + if (rv == APR_ENOTIMPL) { + const char *s; + apr_size_t n; + + rv = apr_bucket_read(e, &s, &n, APR_BLOCK_READ); + if (rv == APR_SUCCESS) { + rv = apr_bucket_setaside(e, p); + } + } + + if (rv != APR_SUCCESS) { + /* Return an error but still save the brigade if + * ->setaside() is really not implemented. */ + if (rv != APR_ENOTIMPL) { + return rv; + } + } + + APR_BUCKET_REMOVE(e); + APR_BRIGADE_INSERT_TAIL(to, e); + } + return APR_SUCCESS; +} + +apr_status_t h2_append_brigade(apr_bucket_brigade *to, + apr_bucket_brigade *from, + apr_off_t *plen, + int *peos) +{ + apr_bucket *e; + apr_off_t len = 0, remain = *plen; + apr_status_t rv; + + *peos = 0; + + while (!APR_BRIGADE_EMPTY(from)) { + e = APR_BRIGADE_FIRST(from); + + if (APR_BUCKET_IS_METADATA(e)) { + if (APR_BUCKET_IS_EOS(e)) { + *peos = 1; + } + } + else { + if (remain > 0 && e->length == ((apr_size_t)-1)) { + const char *ign; + apr_size_t ilen; + rv = apr_bucket_read(e, &ign, &ilen, APR_BLOCK_READ); + if (rv != APR_SUCCESS) { + return rv; + } + } + + if (remain < e->length) { + if (remain <= 0) { + return APR_SUCCESS; + } + apr_bucket_split(e, remain); + } + } + + APR_BUCKET_REMOVE(e); + APR_BRIGADE_INSERT_TAIL(to, e); + len += e->length; + remain -= e->length; + } + + *plen = len; + return APR_SUCCESS; +} + apr_off_t h2_brigade_mem_size(apr_bucket_brigade *bb) { apr_bucket *b; diff --git a/modules/http2/h2_util.h b/modules/http2/h2_util.h index a83f362ffc..4ca2f9b65b 100644 --- a/modules/http2/h2_util.h +++ b/modules/http2/h2_util.h @@ -243,18 +243,44 @@ void h2_util_bb_log(conn_rec *c, int stream_id, int level, /** * Transfer buckets from one brigade to another with a limit on the - * maximum amount of bytes transfered. + * maximum amount of bytes transfered. Sets aside the buckets to + * pool p. * @param to brigade to transfer buckets to * @param from brigades to remove buckets from * @param p pool that buckets should be setaside to * @param plen maximum bytes to transfer, actual bytes transferred * @param peos if an EOS bucket was transferred */ +apr_status_t h2_ltransfer_brigade(apr_bucket_brigade *to, + apr_bucket_brigade *from, + apr_pool_t *p, + apr_off_t *plen, + int *peos); + +/** + * Transfer all buckets from one brigade to another. Sets aside the buckets to + * pool p. + * @param to brigade to transfer buckets to + * @param from brigades to remove buckets from + * @param p pool that buckets should be setaside to + */ apr_status_t h2_transfer_brigade(apr_bucket_brigade *to, apr_bucket_brigade *from, - apr_pool_t *p, - apr_off_t *plen, - int *peos); + apr_pool_t *p); + +/** + * Transfer buckets from one brigade to another with a limit on the + * maximum amount of bytes transfered. Does no setaside magic, lifetime + * of brigades must fit. + * @param to brigade to transfer buckets to + * @param from brigades to remove buckets from + * @param plen maximum bytes to transfer, actual bytes transferred + * @param peos if an EOS bucket was transferred + */ +apr_status_t h2_append_brigade(apr_bucket_brigade *to, + apr_bucket_brigade *from, + apr_off_t *plen, + int *peos); /** * Get an approximnation of the memory footprint of the given diff --git a/modules/http2/h2_version.h b/modules/http2/h2_version.h index 81e4d1a3cf..eb63a32d2b 100644 --- a/modules/http2/h2_version.h +++ b/modules/http2/h2_version.h @@ -26,7 +26,7 @@ * @macro * Version number of the http2 module as c string */ -#define MOD_HTTP2_VERSION "1.4.5-DEV" +#define MOD_HTTP2_VERSION "1.4.6-DEV" /** * @macro @@ -34,7 +34,7 @@ * 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. */ -#define MOD_HTTP2_VERSION_NUM 0x010405 +#define MOD_HTTP2_VERSION_NUM 0x010406 #endif /* mod_h2_h2_version_h */ -- 2.40.0