From 4dbe24518ace95b32eeef4fb508fcfa8c41403ae Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 25 Feb 2016 13:14:30 +0000 Subject: [PATCH] goodbye h2_stream_set, hello h2_ihash in h2_util git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1732295 13f79535-47bb-0310-9956-ffa450edef68 --- CMakeLists.txt | 2 +- modules/http2/NWGNUmod_http2 | 1 - modules/http2/config2.m4 | 1 - modules/http2/h2_conn.c | 1 - modules/http2/h2_filter.c | 3 +- modules/http2/h2_mplx.c | 5 +- modules/http2/h2_mplx.h | 4 +- modules/http2/h2_session.c | 71 +++++++++++----- modules/http2/h2_session.h | 3 +- modules/http2/h2_stream.c | 10 +-- modules/http2/h2_stream.h | 8 +- modules/http2/h2_stream_set.c | 145 -------------------------------- modules/http2/h2_stream_set.h | 51 ----------- modules/http2/h2_util.c | 66 +++++++++++++++ modules/http2/h2_util.h | 85 +++++++++++++++---- modules/http2/mod_http2.dsp | 4 - modules/http2/mod_proxy_http2.c | 66 +++++++++------ 17 files changed, 240 insertions(+), 286 deletions(-) delete mode 100644 modules/http2/h2_stream_set.c delete mode 100644 modules/http2/h2_stream_set.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d9fc914a38..bd3fe405f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -407,7 +407,7 @@ SET(mod_http2_extra_sources modules/http2/h2_mplx.c modules/http2/h2_push.c modules/http2/h2_request.c modules/http2/h2_response.c modules/http2/h2_session.c modules/http2/h2_stream.c - modules/http2/h2_stream_set.c modules/http2/h2_switch.c + modules/http2/h2_switch.c modules/http2/h2_task.c modules/http2/h2_task_input.c modules/http2/h2_task_output.c modules/http2/h2_int_queue.c modules/http2/h2_util.c modules/http2/h2_worker.c diff --git a/modules/http2/NWGNUmod_http2 b/modules/http2/NWGNUmod_http2 index 0e53b4ae1f..12811bd270 100644 --- a/modules/http2/NWGNUmod_http2 +++ b/modules/http2/NWGNUmod_http2 @@ -203,7 +203,6 @@ FILES_nlm_objs = \ $(OBJDIR)/h2_response.o \ $(OBJDIR)/h2_session.o \ $(OBJDIR)/h2_stream.o \ - $(OBJDIR)/h2_stream_set.o \ $(OBJDIR)/h2_switch.o \ $(OBJDIR)/h2_task.o \ $(OBJDIR)/h2_task_input.o \ diff --git a/modules/http2/config2.m4 b/modules/http2/config2.m4 index 85a635e6b3..b47b2d2192 100644 --- a/modules/http2/config2.m4 +++ b/modules/http2/config2.m4 @@ -38,7 +38,6 @@ h2_request.lo dnl h2_response.lo dnl h2_session.lo dnl h2_stream.lo dnl -h2_stream_set.lo dnl h2_switch.lo dnl h2_task.lo dnl h2_task_input.lo dnl diff --git a/modules/http2/h2_conn.c b/modules/http2/h2_conn.c index ac0379b329..f6f814e634 100644 --- a/modules/http2/h2_conn.c +++ b/modules/http2/h2_conn.c @@ -32,7 +32,6 @@ #include "h2_mplx.h" #include "h2_session.h" #include "h2_stream.h" -#include "h2_stream_set.h" #include "h2_h2.h" #include "h2_task.h" #include "h2_worker.h" diff --git a/modules/http2/h2_filter.c b/modules/http2/h2_filter.c index 8330ace150..751480e73f 100644 --- a/modules/http2/h2_filter.c +++ b/modules/http2/h2_filter.c @@ -28,7 +28,6 @@ #include "h2_push.h" #include "h2_task.h" #include "h2_stream.h" -#include "h2_stream_set.h" #include "h2_request.h" #include "h2_response.h" #include "h2_session.h" @@ -217,7 +216,7 @@ static apr_status_t h2_sos_h2_status_buffer(h2_sos *sos, apr_bucket_brigade *bb) bbout(" \"session_id\": %ld,\n", (long)session->id); bbout(" \"streams_max\": %d,\n", (int)session->max_stream_count); bbout(" \"this_stream\": %d,\n", stream->id); - bbout(" \"streams_open\": %d,\n", (int)h2_stream_set_size(session->streams)); + bbout(" \"streams_open\": %d,\n", (int)h2_ihash_count(session->streams)); bbout(" \"max_stream_started\": %d,\n", mplx->max_stream_started); bbout(" \"requests_received\": %d,\n", session->requests_received); bbout(" \"responses_submitted\": %d,\n", session->responses_submitted); diff --git a/modules/http2/h2_mplx.c b/modules/http2/h2_mplx.c index 47e222f3d6..90765f58d7 100644 --- a/modules/http2/h2_mplx.c +++ b/modules/http2/h2_mplx.c @@ -41,7 +41,6 @@ #include "h2_mplx.h" #include "h2_request.h" #include "h2_stream.h" -#include "h2_stream_set.h" #include "h2_task.h" #include "h2_task_input.h" #include "h2_task_output.h" @@ -640,7 +639,7 @@ apr_status_t h2_mplx_out_read_to(h2_mplx *m, int stream_id, return status; } -h2_stream *h2_mplx_next_submit(h2_mplx *m, h2_stream_set *streams) +h2_stream *h2_mplx_next_submit(h2_mplx *m, h2_ihash_t *streams) { apr_status_t status; h2_stream *stream = NULL; @@ -650,7 +649,7 @@ h2_stream *h2_mplx_next_submit(h2_mplx *m, h2_stream_set *streams) if ((status = enter_mutex(m, &acquired)) == APR_SUCCESS) { h2_io *io = h2_io_set_shift(m->ready_ios); if (io && !m->aborted) { - stream = h2_stream_set_get(streams, io->id); + stream = h2_ihash_get(streams, io->id); if (stream) { if (io->rst_error) { h2_stream_rst(stream, io->rst_error); diff --git a/modules/http2/h2_mplx.h b/modules/http2/h2_mplx.h index aebd672e04..12bb2d39ac 100644 --- a/modules/http2/h2_mplx.h +++ b/modules/http2/h2_mplx.h @@ -38,6 +38,7 @@ struct apr_pool_t; struct apr_thread_mutex_t; struct apr_thread_cond_t; struct h2_config; +struct h2_ihash_t; struct h2_response; struct h2_task; struct h2_stream; @@ -45,7 +46,6 @@ struct h2_request; struct h2_io_set; struct apr_thread_cond_t; struct h2_workers; -struct h2_stream_set; struct h2_int_queue; struct h2_req_engine; @@ -267,7 +267,7 @@ apr_status_t h2_mplx_in_update_windows(h2_mplx *m); * @param bb the brigade to place any existing repsonse body data into */ struct h2_stream *h2_mplx_next_submit(h2_mplx *m, - struct h2_stream_set *streams); + struct h2_ihash_t *streams); /** * Reads output data from the given stream. Will never block, but diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c index 9b24e621c4..0be64b8cd7 100644 --- a/modules/http2/h2_session.c +++ b/modules/http2/h2_session.c @@ -38,7 +38,6 @@ #include "h2_request.h" #include "h2_response.h" #include "h2_stream.h" -#include "h2_stream_set.h" #include "h2_from_h1.h" #include "h2_task.h" #include "h2_session.h" @@ -94,7 +93,7 @@ h2_stream *h2_session_open_stream(h2_session *session, int stream_id) stream = h2_stream_open(stream_id, stream_pool, session); - h2_stream_set_add(session->streams, stream); + h2_ihash_add(session->streams, stream); if (H2_STREAM_CLIENT_INITIATED(stream_id) && stream_id > session->max_stream_received) { ++session->requests_received; @@ -666,17 +665,13 @@ static void h2_session_destroy(h2_session *session) if (APLOGctrace1(session->c)) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, "h2_session(%ld): destroy, %d streams open", - session->id, (int)h2_stream_set_size(session->streams)); + session->id, (int)h2_ihash_count(session->streams)); } if (session->mplx) { h2_mplx_set_consumed_cb(session->mplx, NULL, NULL); h2_mplx_release_and_join(session->mplx, session->iowait); session->mplx = NULL; } - if (session->streams) { - h2_stream_set_destroy(session->streams); - session->streams = NULL; - } if (session->pool) { apr_pool_destroy(session->pool); } @@ -822,8 +817,7 @@ static h2_session *h2_session_create_int(conn_rec *c, return NULL; } - session->streams = h2_stream_set_create(session->pool, session->max_stream_count); - + session->streams = h2_ihash_create(session->pool,offsetof(h2_stream, id)); session->workers = workers; session->mplx = h2_mplx_create(c, session->pool, session->config, session->s->timeout, workers); @@ -1032,8 +1026,9 @@ typedef struct { int resume_count; } resume_ctx; -static int resume_on_data(void *ctx, h2_stream *stream) +static int resume_on_data(void *ctx, void *val) { + h2_stream *stream = val; resume_ctx *rctx = (resume_ctx*)ctx; h2_session *session = rctx->session; AP_DEBUG_ASSERT(session); @@ -1059,7 +1054,7 @@ static int resume_on_data(void *ctx, h2_stream *stream) static int h2_session_resume_streams_with_data(h2_session *session) { AP_DEBUG_ASSERT(session); - if (!h2_stream_set_is_empty(session->streams) + if (!h2_ihash_is_empty(session->streams) && session->mplx && !session->mplx->aborted) { resume_ctx ctx; @@ -1068,7 +1063,7 @@ static int h2_session_resume_streams_with_data(h2_session *session) /* Resume all streams where we have data in the out queue and * which had been suspended before. */ - h2_stream_set_iter(session->streams, resume_on_data, &ctx); + h2_ihash_iter(session->streams, resume_on_data, &ctx); return ctx.resume_count; } return 0; @@ -1077,7 +1072,7 @@ static int h2_session_resume_streams_with_data(h2_session *session) h2_stream *h2_session_get_stream(h2_session *session, int stream_id) { if (!session->last_stream || stream_id != session->last_stream->id) { - session->last_stream = h2_stream_set_get(session->streams, stream_id); + session->last_stream = h2_ihash_get(session->streams, stream_id); } return session->last_stream; } @@ -1447,7 +1442,7 @@ apr_status_t h2_session_stream_destroy(h2_session *session, h2_stream *stream) session->last_stream = NULL; } if (session->streams) { - h2_stream_set_remove(session->streams, stream->id); + h2_ihash_remove(session->streams, stream->id); } h2_stream_destroy(stream); @@ -1592,12 +1587,46 @@ static apr_status_t h2_session_read(h2_session *session, int block) return rstatus; } +static int unsubmitted_iter(void *ctx, void *val) +{ + h2_stream *stream = val; + if (h2_stream_needs_submit(stream)) { + *((int *)ctx) = 1; + return 0; + } + return 1; +} + +static int has_unsubmitted_streams(h2_session *session) +{ + int has_unsubmitted = 0; + h2_ihash_iter(session->streams, unsubmitted_iter, &has_unsubmitted); + return has_unsubmitted; +} + +static int suspended_iter(void *ctx, void *val) +{ + h2_stream *stream = val; + if (h2_stream_is_suspended(stream)) { + *((int *)ctx) = 1; + return 0; + } + return 1; +} + +static int has_suspended_streams(h2_session *session) +{ + int has_suspended = 0; + h2_ihash_iter(session->streams, suspended_iter, &has_suspended); + return has_suspended; +} + static apr_status_t h2_session_submit(h2_session *session) { apr_status_t status = APR_EAGAIN; h2_stream *stream; - if (h2_stream_set_has_unsubmitted(session->streams)) { + if (has_unsubmitted_streams(session)) { /* If we have responses ready, submit them now. */ while ((stream = h2_mplx_next_submit(session->mplx, session->streams))) { status = submit_response(session, stream); @@ -1766,7 +1795,7 @@ static void h2_session_ev_no_io(h2_session *session, int arg, const char *msg) * CPU cycles. Ideally, we'd like to do a blocking read, but that * is not possible if we have scheduled tasks and wait * for them to produce something. */ - if (h2_stream_set_is_empty(session->streams)) { + if (h2_ihash_is_empty(session->streams)) { if (!is_accepting_streams(session)) { /* We are no longer accepting new streams and have * finished processing existing ones. Time to leave. */ @@ -1782,8 +1811,8 @@ static void h2_session_ev_no_io(h2_session *session, int arg, const char *msg) session->s->timeout) + apr_time_now(); } } - else if (!h2_stream_set_has_unsubmitted(session->streams) - && !h2_stream_set_has_suspended(session->streams)) { + else if (!has_unsubmitted_streams(session) + && !has_suspended_streams(session)) { /* none of our streams is waiting for a response or * new output data from task processing, * switch to blocking reads. We are probably waiting on @@ -1927,7 +1956,7 @@ static void update_child_status(h2_session *session, int status, const char *msg apr_snprintf(session->status, sizeof(session->status), "%s, streams: %d/%d/%d/%d/%d (open/recv/resp/push/rst)", msg? msg : "-", - (int)h2_stream_set_size(session->streams), + (int)h2_ihash_count(session->streams), (int)session->requests_received, (int)session->responses_submitted, (int)session->pushes_submitted, @@ -1982,7 +2011,7 @@ apr_status_t h2_session_process(h2_session *session, int async) break; case H2_SESSION_ST_IDLE: - no_streams = h2_stream_set_is_empty(session->streams); + no_streams = h2_ihash_is_empty(session->streams); update_child_status(session, (no_streams? SERVER_BUSY_KEEPALIVE : SERVER_BUSY_READ), "idle"); if (async && !session->r && session->requests_received && no_streams) { @@ -2066,7 +2095,7 @@ apr_status_t h2_session_process(h2_session *session, int async) } } - if (!h2_stream_set_is_empty(session->streams)) { + if (!h2_ihash_is_empty(session->streams)) { /* resume any streams for which data is available again */ h2_session_resume_streams_with_data(session); /* Submit any responses/push_promises that are ready */ diff --git a/modules/http2/h2_session.h b/modules/http2/h2_session.h index 118be3c954..d07ae4cc08 100644 --- a/modules/http2/h2_session.h +++ b/modules/http2/h2_session.h @@ -44,6 +44,7 @@ struct apr_thread_cond_t; struct h2_ctx; struct h2_config; struct h2_filter_cin; +struct h2_ihash_t; struct h2_mplx; struct h2_priority; struct h2_push; @@ -118,7 +119,7 @@ typedef struct h2_session { struct h2_mplx *mplx; /* multiplexer for stream data */ struct h2_stream *last_stream; /* last stream worked with */ - struct h2_stream_set *streams; /* streams handled by this session */ + struct h2_ihash_t *streams; /* streams handled by this session */ apr_pool_t *spare; /* spare stream pool */ diff --git a/modules/http2/h2_stream.c b/modules/http2/h2_stream.c index fc2d021ed8..8af65673a4 100644 --- a/modules/http2/h2_stream.c +++ b/modules/http2/h2_stream.c @@ -120,7 +120,7 @@ static int close_output(h2_stream *stream) return 1; } -static int input_open(h2_stream *stream) +static int input_open(const h2_stream *stream) { switch (stream->state) { case H2_STREAM_ST_OPEN: @@ -328,7 +328,7 @@ apr_status_t h2_stream_schedule(h2_stream *stream, int eos, int push_enabled, return status; } -int h2_stream_is_scheduled(h2_stream *stream) +int h2_stream_is_scheduled(const h2_stream *stream) { return stream->scheduled; } @@ -435,7 +435,7 @@ void h2_stream_set_suspended(h2_stream *stream, int suspended) stream->session->id, stream->id, stream->suspended); } -int h2_stream_is_suspended(h2_stream *stream) +int h2_stream_is_suspended(const h2_stream *stream) { AP_DEBUG_ASSERT(stream); return stream->suspended; @@ -479,12 +479,12 @@ apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb, return stream->sos->read_to(stream->sos, bb, plen, peos); } -int h2_stream_input_is_open(h2_stream *stream) +int h2_stream_input_is_open(const h2_stream *stream) { return input_open(stream); } -int h2_stream_needs_submit(h2_stream *stream) +int h2_stream_needs_submit(const h2_stream *stream) { switch (stream->state) { case H2_STREAM_ST_OPEN: diff --git a/modules/http2/h2_stream.h b/modules/http2/h2_stream.h index e3d71a3f9b..7d724259fa 100644 --- a/modules/http2/h2_stream.h +++ b/modules/http2/h2_stream.h @@ -183,7 +183,7 @@ apr_status_t h2_stream_schedule(h2_stream *stream, int eos, int push_enabled, * @param stream the stream to check on * @return != 0 iff stream has been scheduled */ -int h2_stream_is_scheduled(h2_stream *stream); +int h2_stream_is_scheduled(const h2_stream *stream); struct h2_response *h2_stream_get_response(h2_stream *stream); @@ -270,21 +270,21 @@ void h2_stream_set_suspended(h2_stream *stream, int suspended); * @param stream the stream to check * @return != 0 iff stream is suspended. */ -int h2_stream_is_suspended(h2_stream *stream); +int h2_stream_is_suspended(const h2_stream *stream); /** * Check if the stream has open input. * @param stream the stream to check * @return != 0 iff stream has open input. */ -int h2_stream_input_is_open(h2_stream *stream); +int h2_stream_input_is_open(const h2_stream *stream); /** * Check if the stream has not submitted a response or RST yet. * @param stream the stream to check * @return != 0 iff stream has not submitted a response or RST. */ -int h2_stream_needs_submit(h2_stream *stream); +int h2_stream_needs_submit(const h2_stream *stream); /** * Submit any server push promises on this stream and schedule diff --git a/modules/http2/h2_stream_set.c b/modules/http2/h2_stream_set.c deleted file mode 100644 index aa0f8c6501..0000000000 --- a/modules/http2/h2_stream_set.c +++ /dev/null @@ -1,145 +0,0 @@ -/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include -#include - -#include -#include - -#include "h2_private.h" -#include "h2_stream.h" -#include "h2_stream_set.h" - - -struct h2_stream_set { - apr_hash_t *hash; -}; - -static unsigned int stream_hash(const char *key, apr_ssize_t *klen) -{ - return (unsigned int)(*((int*)key)); -} - -h2_stream_set *h2_stream_set_create(apr_pool_t *pool, int max) -{ - h2_stream_set *sp = apr_pcalloc(pool, sizeof(h2_stream_set)); - sp->hash = apr_hash_make_custom(pool, stream_hash); - - return sp; -} - -void h2_stream_set_destroy(h2_stream_set *sp) -{ - (void)sp; -} - -h2_stream *h2_stream_set_get(h2_stream_set *sp, int stream_id) -{ - return apr_hash_get(sp->hash, &stream_id, sizeof(stream_id)); -} - -void h2_stream_set_add(h2_stream_set *sp, h2_stream *stream) -{ - apr_hash_set(sp->hash, &stream->id, sizeof(stream->id), stream); -} - -void h2_stream_set_remove(h2_stream_set *sp, int stream_id) -{ - apr_hash_set(sp->hash, &stream_id, sizeof(stream_id), NULL); -} - -int h2_stream_set_is_empty(h2_stream_set *sp) -{ - return apr_hash_count(sp->hash) == 0; -} - -apr_size_t h2_stream_set_size(h2_stream_set *sp) -{ - return apr_hash_count(sp->hash); -} - -typedef struct { - h2_stream_set_iter_fn *iter; - void *ctx; -} iter_ctx; - -static int hash_iter(void *ctx, const void *key, apr_ssize_t klen, - const void *val) -{ - iter_ctx *ictx = ctx; - return ictx->iter(ictx->ctx, (h2_stream*)val); -} - -void h2_stream_set_iter(h2_stream_set *sp, - h2_stream_set_iter_fn *iter, void *ctx) -{ - iter_ctx ictx; - ictx.iter = iter; - ictx.ctx = ctx; - apr_hash_do(hash_iter, &ictx, sp->hash); -} - -static int unsubmitted_iter(void *ctx, h2_stream *stream) -{ - if (h2_stream_needs_submit(stream)) { - *((int *)ctx) = 1; - return 0; - } - return 1; -} - -int h2_stream_set_has_unsubmitted(h2_stream_set *sp) -{ - int has_unsubmitted = 0; - h2_stream_set_iter(sp, unsubmitted_iter, &has_unsubmitted); - return has_unsubmitted; -} - -static int input_open_iter(void *ctx, h2_stream *stream) -{ - if (h2_stream_input_is_open(stream)) { - *((int *)ctx) = 1; - return 0; - } - return 1; -} - -int h2_stream_set_has_open_input(h2_stream_set *sp) -{ - int has_input_open = 0; - h2_stream_set_iter(sp, input_open_iter, &has_input_open); - return has_input_open; -} - -static int suspended_iter(void *ctx, h2_stream *stream) -{ - if (h2_stream_is_suspended(stream)) { - *((int *)ctx) = 1; - return 0; - } - return 1; -} - -int h2_stream_set_has_suspended(h2_stream_set *sp) -{ - int has_suspended = 0; - h2_stream_set_iter(sp, suspended_iter, &has_suspended); - return has_suspended; -} - diff --git a/modules/http2/h2_stream_set.h b/modules/http2/h2_stream_set.h deleted file mode 100644 index d0041c4843..0000000000 --- a/modules/http2/h2_stream_set.h +++ /dev/null @@ -1,51 +0,0 @@ -/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __mod_h2__h2_stream_set__ -#define __mod_h2__h2_stream_set__ - -/** - * A set of h2_stream instances. Allows lookup by stream id - * and other criteria. - */ - -typedef h2_stream *h2_stream_set_match_fn(void *ctx, h2_stream *stream); -typedef int h2_stream_set_iter_fn(void *ctx, h2_stream *stream); - -typedef struct h2_stream_set h2_stream_set; - - -h2_stream_set *h2_stream_set_create(apr_pool_t *pool, int max); - -void h2_stream_set_destroy(h2_stream_set *sp); - -void h2_stream_set_add(h2_stream_set *sp, h2_stream *stream); - -h2_stream *h2_stream_set_get(h2_stream_set *sp, int stream_id); - -void h2_stream_set_remove(h2_stream_set *sp, int stream_id); - -void h2_stream_set_iter(h2_stream_set *sp, - h2_stream_set_iter_fn *iter, void *ctx); - -int h2_stream_set_is_empty(h2_stream_set *sp); - -apr_size_t h2_stream_set_size(h2_stream_set *sp); - -int h2_stream_set_has_unsubmitted(h2_stream_set *sp); -int h2_stream_set_has_open_input(h2_stream_set *sp); -int h2_stream_set_has_suspended(h2_stream_set *sp); - -#endif /* defined(__mod_h2__h2_stream_set__) */ diff --git a/modules/http2/h2_util.c b/modules/http2/h2_util.c index b64683ab17..1cbe8d1e8e 100644 --- a/modules/http2/h2_util.c +++ b/modules/http2/h2_util.c @@ -231,6 +231,72 @@ const char *h2_util_first_token_match(apr_pool_t *pool, const char *s, } +/******************************************************************************* + * ihash - hash for structs with int identifier + ******************************************************************************/ +struct h2_ihash_t { + apr_hash_t *hash; + size_t ioff; +}; + +static unsigned int ihash(const char *key, apr_ssize_t *klen) +{ + return (unsigned int)(*((int*)key)); +} + +h2_ihash_t *h2_ihash_create(apr_pool_t *pool, size_t offset_of_int) +{ + h2_ihash_t *ih = apr_pcalloc(pool, sizeof(h2_ihash_t)); + ih->hash = apr_hash_make_custom(pool, ihash); + ih->ioff = offset_of_int; + return ih; +} + +size_t h2_ihash_count(h2_ihash_t *ih) +{ + return apr_hash_count(ih->hash); +} + +int h2_ihash_is_empty(h2_ihash_t *ih) +{ + return apr_hash_count(ih->hash) == 0; +} + +void *h2_ihash_get(h2_ihash_t *ih, int id) +{ + return apr_hash_get(ih->hash, &id, sizeof(id)); +} + +typedef struct { + h2_ihash_iter_t *iter; + void *ctx; +} iter_ctx; + +static int ihash_iter(void *ctx, const void *key, apr_ssize_t klen, + const void *val) +{ + iter_ctx *ictx = ctx; + return ictx->iter(ictx->ctx, (void*)val); /* why is this passed const?*/ +} + +void h2_ihash_iter(h2_ihash_t *ih, h2_ihash_iter_t *fn, void *ctx) +{ + iter_ctx ictx; + ictx.iter = fn; + ictx.ctx = ctx; + apr_hash_do(ihash_iter, &ictx, ih->hash); +} + +void h2_ihash_add(h2_ihash_t *ih, void *val) +{ + apr_hash_set(ih->hash, ((char *)val + ih->ioff), sizeof(int), val); +} + +void h2_ihash_remove(h2_ihash_t *ih, int id) +{ + apr_hash_set(ih->hash, &id, sizeof(id), NULL); +} + /******************************************************************************* * h2_util for apt_table_t ******************************************************************************/ diff --git a/modules/http2/h2_util.h b/modules/http2/h2_util.h index 218a57fddf..e13a0dc2f2 100644 --- a/modules/http2/h2_util.h +++ b/modules/http2/h2_util.h @@ -16,6 +16,9 @@ #ifndef __mod_h2__h2_util__ #define __mod_h2__h2_util__ +/******************************************************************************* + * some debugging/format helpers + ******************************************************************************/ struct h2_request; struct nghttp2_frame; @@ -30,6 +33,38 @@ void h2_util_camel_case_header(char *s, size_t len); int h2_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen); +/******************************************************************************* + * ihash - hash for structs with int identifier + ******************************************************************************/ +typedef struct h2_ihash_t h2_ihash_t; +typedef int h2_ihash_iter_t(void *ctx, void *val); + +/** + * Create a hash for structures that have an identifying int member. + * @param pool the pool to use + * @param offset_of_int the offsetof() the int member in the struct + */ +h2_ihash_t *h2_ihash_create(apr_pool_t *pool, size_t offset_of_int); + +size_t h2_ihash_count(h2_ihash_t *ih); +int h2_ihash_is_empty(h2_ihash_t *ih); +void *h2_ihash_get(h2_ihash_t *ih, int id); + +/** + * Iterate over the hash members (without defined order) and invoke + * fn for each member until 0 is returned. + * @param ih the hash to iterate over + * @param fn the function to invoke on each member + * @param ctx user supplied data passed into each iteration call + */ +void h2_ihash_iter(h2_ihash_t *ih, h2_ihash_iter_t *fn, void *ctx); + +void h2_ihash_add(h2_ihash_t *ih, void *val); +void h2_ihash_remove(h2_ihash_t *ih, int id); + +/******************************************************************************* + * common helpers + ******************************************************************************/ /** * Count the bytes that all key/value pairs in a table have * in length (exlucding terminating 0s), plus additional extra per pair. @@ -40,11 +75,6 @@ int h2_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen) */ apr_size_t h2_util_table_bytes(apr_table_t *t, apr_size_t pair_extra); -int h2_req_ignore_header(const char *name, size_t len); -int h2_req_ignore_trailer(const char *name, size_t len); -int h2_res_ignore_trailer(const char *name, size_t len); -int h2_proxy_res_ignore_header(const char *name, size_t len); - /** * Return != 0 iff the string s contains the token, as specified in * HTTP header syntax, rfc7230. @@ -54,6 +84,32 @@ int h2_util_contains_token(apr_pool_t *pool, const char *s, const char *token); const char *h2_util_first_token_match(apr_pool_t *pool, const char *s, const char *tokens[], apr_size_t len); +/** Match a header value against a string constance, case insensitive */ +#define H2_HD_MATCH_LIT(l, name, nlen) \ + ((nlen == sizeof(l) - 1) && !apr_strnatcasecmp(l, name)) + +/******************************************************************************* + * HTTP/2 header helpers + ******************************************************************************/ +int h2_req_ignore_header(const char *name, size_t len); +int h2_req_ignore_trailer(const char *name, size_t len); +int h2_res_ignore_trailer(const char *name, size_t len); +int h2_proxy_res_ignore_header(const char *name, size_t len); + +/** + * Set the push policy for the given request. Takes request headers into + * account, see draft https://tools.ietf.org/html/draft-ruellan-http-accept-push-policy-00 + * for details. + * + * @param req the request to determine the policy for + * @param p the pool to use + * @param push_enabled if HTTP/2 server push is generally enabled for this request + */ +void h2_push_policy_determine(struct h2_request *req, apr_pool_t *p, int push_enabled); + +/******************************************************************************* + * base64 url encoding, different table from normal base64 + ******************************************************************************/ /** * I always wanted to write my own base64url decoder...not. See * https://tools.ietf.org/html/rfc4648#section-5 for description. @@ -64,8 +120,9 @@ apr_size_t h2_util_base64url_decode(const char **decoded, const char *h2_util_base64url_encode(const char *data, apr_size_t len, apr_pool_t *pool); -#define H2_HD_MATCH_LIT(l, name, nlen) \ - ((nlen == sizeof(l) - 1) && !apr_strnatcasecmp(l, name)) +/******************************************************************************* + * nghttp2 helpers + ******************************************************************************/ #define H2_HD_MATCH_LIT_CS(l, name) \ ((strlen(name) == sizeof(l) - 1) && !apr_strnatcasecmp(l, name)) @@ -99,6 +156,9 @@ h2_ngheader *h2_util_ngheader_make_res(apr_pool_t *p, h2_ngheader *h2_util_ngheader_make_req(apr_pool_t *p, const struct h2_request *req); +/******************************************************************************* + * apr brigade helpers + ******************************************************************************/ /** * Moves data from one brigade into another. If maxlen > 0, it only * moves up to maxlen bytes into the target brigade, making bucket splits @@ -192,15 +252,4 @@ apr_status_t h2_transfer_brigade(apr_bucket_brigade *to, apr_off_t *plen, int *peos); -/** - * Set the push policy for the given request. Takes request headers into - * account, see draft https://tools.ietf.org/html/draft-ruellan-http-accept-push-policy-00 - * for details. - * - * @param req the request to determine the policy for - * @param p the pool to use - * @param push_enabled if HTTP/2 server push is generally enabled for this request - */ -void h2_push_policy_determine(struct h2_request *req, apr_pool_t *p, int push_enabled); - #endif /* defined(__mod_h2__h2_util__) */ diff --git a/modules/http2/mod_http2.dsp b/modules/http2/mod_http2.dsp index 0431548a96..3bfb09d257 100644 --- a/modules/http2/mod_http2.dsp +++ b/modules/http2/mod_http2.dsp @@ -177,10 +177,6 @@ SOURCE=./h2_stream.c # End Source File # Begin Source File -SOURCE=./h2_stream_set.c -# End Source File -# Begin Source File - SOURCE=./h2_switch.c # End Source File # Begin Source File diff --git a/modules/http2/mod_proxy_http2.c b/modules/http2/mod_proxy_http2.c index 3cdf44ae74..efd69465c2 100644 --- a/modules/http2/mod_proxy_http2.c +++ b/modules/http2/mod_proxy_http2.c @@ -233,11 +233,27 @@ static void request_done(h2_proxy_session *session, request_rec *r) } } +static request_rec *next_request(h2_proxy_ctx *ctx, h2_proxy_session *session, + request_rec *r) +{ + if (!r && !ctx->standalone) { + ctx->engine->capacity = session->remote_max_concurrent; + if (req_engine_pull(ctx->engine, APR_NONBLOCK_READ, &r) == APR_SUCCESS) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_proxy_session(%s): pulled request %s", + session->id, r->the_request); + } + } + return r; +} + static apr_status_t proxy_engine_run(h2_proxy_ctx *ctx, request_rec *r) { apr_status_t status = OK; h2_proxy_session *session; -setup_session: +setup_backend: + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->owner, + "eng(%s): setup backend", ctx->engine->id); /* Step Two: Make the Connection (or check that an already existing * socket is still usable). On success, we have a socket connected to * backend->hostname. */ @@ -280,6 +296,8 @@ setup_session: /* Step Four: Send the Request in a new HTTP/2 stream and * loop until we got the response or encounter errors. */ + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->owner, + "eng(%s): setup session", ctx->engine->id); session = h2_proxy_session_setup(ctx->engine->id, ctx->p_conn, ctx->conf, request_done); if (!session) { @@ -289,39 +307,34 @@ setup_session: } run_session: + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->owner, + "eng(%s): run session %s", ctx->engine->id, session->id); session->user_data = ctx; - add_request(session, r); - status = APR_EAGAIN; + status = h2_proxy_session_process(session); while (APR_STATUS_IS_EAGAIN(status)) { - status = h2_proxy_session_process(session); - - if (APR_STATUS_IS_EAGAIN(status) && !ctx->standalone) { - ctx->engine->capacity = session->remote_max_concurrent; - if (req_engine_pull(ctx->engine, APR_NONBLOCK_READ, &r) == APR_SUCCESS) { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, - "h2_proxy_session(%s): pulled request %s", - session->id, r->the_request); - add_request(session, r); - } + r = next_request(ctx, session, r); + if (r) { + add_request(session, r); + r = NULL; } + + status = h2_proxy_session_process(session); } + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, ctx->owner, + "eng(%s): end of session run", ctx->engine->id); if (session->state == H2_PROXYS_ST_DONE || status != APR_SUCCESS) { ctx->p_conn->close = 1; } - if (!ctx->standalone) { - ctx->engine->capacity = session->remote_max_concurrent; - if (req_engine_pull(ctx->engine, APR_NONBLOCK_READ, &r) == APR_SUCCESS) { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, - "h2_proxy_session(%s): idle, pulled request %s", - session->id, r->the_request); - add_request(session, r); - if (ctx->p_conn->close) { - goto setup_session; - } - goto run_session; + r = next_request(ctx, session, r); + if (r) { + if (ctx->p_conn->close) { + goto setup_backend; } + add_request(session, r); + r = NULL; + goto run_session; } if (session->streams && !h2_iq_empty(session->streams)) { @@ -330,8 +343,9 @@ run_session: "session run done with %d streams unfinished", h2_iq_size(session->streams)); } - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, - ctx->p_conn->connection, "session run done"); + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, + ctx->p_conn->connection, "eng(%s): session run done", + ctx->engine->id); session->user_data = NULL; return status; } -- 2.40.0