diff --git a/.gitignore b/.gitignore
index 657ad18..ff5c489 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-SOURCES/httpd-2.4.27.tar.bz2
+SOURCES/httpd-2.4.34.tar.bz2
diff --git a/.httpd24-httpd.metadata b/.httpd24-httpd.metadata
index d0b8573..939981a 100644
--- a/.httpd24-httpd.metadata
+++ b/.httpd24-httpd.metadata
@@ -1 +1 @@
-699e4e917e8fb5fd7d0ce7e009f8256ed02ec6fc SOURCES/httpd-2.4.27.tar.bz2
+94d6e274273903ed153479c7701fa03761abf93d SOURCES/httpd-2.4.34.tar.bz2
diff --git a/SOURCES/00-proxy.conf b/SOURCES/00-proxy.conf
index 448eb63..f0f84c2 100644
--- a/SOURCES/00-proxy.conf
+++ b/SOURCES/00-proxy.conf
@@ -14,4 +14,5 @@ LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_hcheck_module modules/mod_proxy_hcheck.so
LoadModule proxy_scgi_module modules/mod_proxy_scgi.so
+LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
diff --git a/SOURCES/01-md.conf b/SOURCES/01-md.conf
new file mode 100644
index 0000000..2739202
--- /dev/null
+++ b/SOURCES/01-md.conf
@@ -0,0 +1 @@
+LoadModule md_module modules/mod_md.so
diff --git a/SOURCES/httpd-2.4.12-skiplist.patch b/SOURCES/httpd-2.4.12-skiplist.patch
deleted file mode 100644
index e514785..0000000
--- a/SOURCES/httpd-2.4.12-skiplist.patch
+++ /dev/null
@@ -1,1021 +0,0 @@
-diff --git a/server/mpm/event/config.m4 b/server/mpm/event/config.m4
-index c891c75..351f1ac 100644
---- a/server/mpm/event/config.m4
-+++ b/server/mpm/event/config.m4
-@@ -7,8 +7,6 @@ elif test $have_threaded_sig_graceful != yes; then
- AC_MSG_RESULT(no - SIG_GRACEFUL cannot be used with a threaded MPM)
- elif test $ac_cv_have_threadsafe_pollset != yes; then
- AC_MSG_RESULT(no - APR_POLLSET_THREADSAFE is not supported)
--elif test $apr_has_skiplist != yes; then
-- AC_MSG_RESULT(no - APR skiplist is not available, need APR 1.5.x or later)
- else
- AC_MSG_RESULT(yes)
- APACHE_MPM_SUPPORTED(event, yes, yes)
-diff --git a/server/mpm/event/config3.m4 b/server/mpm/event/config3.m4
-index 8aa1631..fa9b8af 100644
---- a/server/mpm/event/config3.m4
-+++ b/server/mpm/event/config3.m4
-@@ -2,6 +2,6 @@ dnl ## XXX - Need a more thorough check of the proper flags to use
-
- APACHE_SUBST(MOD_MPM_EVENT_LDADD)
-
--APACHE_MPM_MODULE(event, $enable_mpm_event, event.lo fdqueue.lo,[
-+APACHE_MPM_MODULE(event, $enable_mpm_event, event.lo fdqueue.lo apr_skiplist.lo,[
- AC_CHECK_FUNCS(pthread_kill)
- ], , [\$(MOD_MPM_EVENT_LDADD)])
-diff --git a/server/mpm/event/apr_skiplist.c b/server/mpm/event/apr_skiplist.c
-new file mode 100644
-index 0000000..b4696bd
---- /dev/null
-+++ b/server/mpm/event/apr_skiplist.c
-@@ -0,0 +1,721 @@
-+/* Licensed to the Apache Software Foundation (ASF) under one or more
-+ * contributor license agreements. See the NOTICE file distributed with
-+ * this work for additional information regarding copyright ownership.
-+ * The ASF licenses this file to You 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.
-+ */
-+
-+/*
-+ * Modified to use APR and APR pools.
-+ * TODO: Is malloc() better? Will long running skiplists grow too much?
-+ * Keep the skiplist_alloc() and skiplist_free() until we know
-+ * Yeah, if using pools it means some bogus cycles for checks
-+ * (and an useless function call for skiplist_free) which we
-+ * can removed if/when needed.
-+ */
-+
-+#include "apr_skiplist.h"
-+
-+typedef struct {
-+ apr_skiplistnode **data;
-+ size_t size, pos;
-+ apr_pool_t *p;
-+} apr_skiplist_q;
-+
-+struct apr_skiplist {
-+ apr_skiplist_compare compare;
-+ apr_skiplist_compare comparek;
-+ int height;
-+ int preheight;
-+ size_t size;
-+ apr_skiplistnode *top;
-+ apr_skiplistnode *bottom;
-+ /* These two are needed for appending */
-+ apr_skiplistnode *topend;
-+ apr_skiplistnode *bottomend;
-+ apr_skiplist *index;
-+ apr_array_header_t *memlist;
-+ apr_skiplist_q nodes_q,
-+ stack_q;
-+ apr_pool_t *pool;
-+};
-+
-+struct apr_skiplistnode {
-+ void *data;
-+ apr_skiplistnode *next;
-+ apr_skiplistnode *prev;
-+ apr_skiplistnode *down;
-+ apr_skiplistnode *up;
-+ apr_skiplistnode *previndex;
-+ apr_skiplistnode *nextindex;
-+ apr_skiplist *sl;
-+};
-+
-+static int get_b_rand(void)
-+{
-+ static int ph = 32; /* More bits than we will ever use */
-+ static int randseq;
-+ if (ph > 31) { /* Num bits in return of rand() */
-+ ph = 0;
-+ randseq = rand();
-+ }
-+ return randseq & (1 << ph++);
-+}
-+
-+typedef struct {
-+ size_t size;
-+ apr_array_header_t *list;
-+} memlist_t;
-+
-+typedef struct {
-+ void *ptr;
-+ char inuse;
-+} chunk_t;
-+
-+APR_DECLARE(void *) apr_skiplist_alloc(apr_skiplist *sl, size_t size)
-+{
-+ if (sl->pool) {
-+ void *ptr;
-+ int found_size = 0;
-+ int i;
-+ chunk_t *newchunk;
-+ memlist_t *memlist = (memlist_t *)sl->memlist->elts;
-+ for (i = 0; i < sl->memlist->nelts; i++) {
-+ if (memlist->size == size) {
-+ int j;
-+ chunk_t *chunk = (chunk_t *)memlist->list->elts;
-+ found_size = 1;
-+ for (j = 0; j < memlist->list->nelts; j++) {
-+ if (!chunk->inuse) {
-+ chunk->inuse = 1;
-+ return chunk->ptr;
-+ }
-+ chunk++;
-+ }
-+ break; /* no free of this size; punt */
-+ }
-+ memlist++;
-+ }
-+ /* no free chunks */
-+ ptr = apr_palloc(sl->pool, size);
-+ if (!ptr) {
-+ return ptr;
-+ }
-+ /*
-+ * is this a new sized chunk? If so, we need to create a new
-+ * array of them. Otherwise, re-use what we already have.
-+ */
-+ if (!found_size) {
-+ memlist = apr_array_push(sl->memlist);
-+ memlist->size = size;
-+ memlist->list = apr_array_make(sl->pool, 20, sizeof(chunk_t));
-+ }
-+ newchunk = apr_array_push(memlist->list);
-+ newchunk->ptr = ptr;
-+ newchunk->inuse = 1;
-+ return ptr;
-+ }
-+ else {
-+ return malloc(size);
-+ }
-+}
-+
-+APR_DECLARE(void) apr_skiplist_free(apr_skiplist *sl, void *mem)
-+{
-+ if (!sl->pool) {
-+ free(mem);
-+ }
-+ else {
-+ int i;
-+ memlist_t *memlist = (memlist_t *)sl->memlist->elts;
-+ for (i = 0; i < sl->memlist->nelts; i++) {
-+ int j;
-+ chunk_t *chunk = (chunk_t *)memlist->list->elts;
-+ for (j = 0; j < memlist->list->nelts; j++) {
-+ if (chunk->ptr == mem) {
-+ chunk->inuse = 0;
-+ return;
-+ }
-+ chunk++;
-+ }
-+ memlist++;
-+ }
-+ }
-+}
-+
-+static apr_status_t skiplist_qpush(apr_skiplist_q *q, apr_skiplistnode *m)
-+{
-+ if (q->pos >= q->size) {
-+ apr_skiplistnode **data;
-+ size_t size = (q->pos) ? q->pos * 2 : 32;
-+ if (q->p) {
-+ data = apr_palloc(q->p, size * sizeof(*data));
-+ if (data) {
-+ memcpy(data, q->data, q->pos * sizeof(*data));
-+ }
-+ }
-+ else {
-+ data = realloc(q->data, size * sizeof(*data));
-+ }
-+ if (!data) {
-+ return APR_ENOMEM;
-+ }
-+ q->data = data;
-+ q->size = size;
-+ }
-+ q->data[q->pos++] = m;
-+ return APR_SUCCESS;
-+}
-+
-+static APR_INLINE apr_skiplistnode *skiplist_qpop(apr_skiplist_q *q)
-+{
-+ return (q->pos > 0) ? q->data[--q->pos] : NULL;
-+}
-+
-+static APR_INLINE void skiplist_qclear(apr_skiplist_q *q)
-+{
-+ q->pos = 0;
-+}
-+
-+static apr_skiplistnode *skiplist_new_node(apr_skiplist *sl)
-+{
-+ apr_skiplistnode *m = skiplist_qpop(&sl->nodes_q);
-+ if (!m) {
-+ if (sl->pool) {
-+ m = apr_palloc(sl->pool, sizeof *m);
-+ }
-+ else {
-+ m = malloc(sizeof *m);
-+ }
-+ }
-+ return m;
-+}
-+
-+static apr_status_t skiplist_free_node(apr_skiplist *sl, apr_skiplistnode *m)
-+{
-+ return skiplist_qpush(&sl->nodes_q, m);
-+}
-+
-+static apr_status_t skiplisti_init(apr_skiplist **s, apr_pool_t *p)
-+{
-+ apr_skiplist *sl;
-+ if (p) {
-+ sl = apr_pcalloc(p, sizeof(apr_skiplist));
-+ sl->memlist = apr_array_make(p, 20, sizeof(memlist_t));
-+ sl->pool = sl->nodes_q.p = sl->stack_q.p = p;
-+ }
-+ else {
-+ sl = calloc(1, sizeof(apr_skiplist));
-+ if (!sl) {
-+ return APR_ENOMEM;
-+ }
-+ }
-+ *s = sl;
-+ return APR_SUCCESS;
-+}
-+
-+static int indexing_comp(void *a, void *b)
-+{
-+ void *ac = (void *) (((apr_skiplist *) a)->compare);
-+ void *bc = (void *) (((apr_skiplist *) b)->compare);
-+ return ((ac < bc) ? -1 : ((ac > bc) ? 1 : 0));
-+}
-+
-+static int indexing_compk(void *ac, void *b)
-+{
-+ void *bc = (void *) (((apr_skiplist *) b)->compare);
-+ return ((ac < bc) ? -1 : ((ac > bc) ? 1 : 0));
-+}
-+
-+APR_DECLARE(apr_status_t) apr_skiplist_init(apr_skiplist **s, apr_pool_t *p)
-+{
-+ apr_skiplist *sl;
-+ skiplisti_init(s, p);
-+ sl = *s;
-+ skiplisti_init(&(sl->index), p);
-+ apr_skiplist_set_compare(sl->index, indexing_comp, indexing_compk);
-+ return APR_SUCCESS;
-+}
-+
-+APR_DECLARE(void) apr_skiplist_set_compare(apr_skiplist *sl,
-+ apr_skiplist_compare comp,
-+ apr_skiplist_compare compk)
-+{
-+ if (sl->compare && sl->comparek) {
-+ apr_skiplist_add_index(sl, comp, compk);
-+ }
-+ else {
-+ sl->compare = comp;
-+ sl->comparek = compk;
-+ }
-+}
-+
-+APR_DECLARE(void) apr_skiplist_add_index(apr_skiplist *sl,
-+ apr_skiplist_compare comp,
-+ apr_skiplist_compare compk)
-+{
-+ apr_skiplistnode *m;
-+ apr_skiplist *ni;
-+ int icount = 0;
-+ apr_skiplist_find(sl->index, (void *)comp, &m);
-+ if (m) {
-+ return; /* Index already there! */
-+ }
-+ skiplisti_init(&ni, sl->pool);
-+ apr_skiplist_set_compare(ni, comp, compk);
-+ /* Build the new index... This can be expensive! */
-+ m = apr_skiplist_insert(sl->index, ni);
-+ while (m->prev) {
-+ m = m->prev;
-+ icount++;
-+ }
-+ for (m = apr_skiplist_getlist(sl); m; apr_skiplist_next(sl, &m)) {
-+ int j = icount - 1;
-+ apr_skiplistnode *nsln;
-+ nsln = apr_skiplist_insert(ni, m->data);
-+ /* skip from main index down list */
-+ while (j > 0) {
-+ m = m->nextindex;
-+ j--;
-+ }
-+ /* insert this node in the indexlist after m */
-+ nsln->nextindex = m->nextindex;
-+ if (m->nextindex) {
-+ m->nextindex->previndex = nsln;
-+ }
-+ nsln->previndex = m;
-+ m->nextindex = nsln;
-+ }
-+}
-+
-+static int skiplisti_find_compare(apr_skiplist *sl, void *data,
-+ apr_skiplistnode **ret,
-+ apr_skiplist_compare comp)
-+{
-+ int count = 0;
-+ apr_skiplistnode *m;
-+ m = sl->top;
-+ while (m) {
-+ if (m->next) {
-+ int compared = comp(data, m->next->data);
-+ if (compared == 0) {
-+ m = m->next;
-+ while (m->down) {
-+ m = m->down;
-+ }
-+ *ret = m;
-+ return count;
-+ }
-+ if (compared > 0) {
-+ m = m->next;
-+ count++;
-+ continue;
-+ }
-+ }
-+ m = m->down;
-+ count++;
-+ }
-+ *ret = NULL;
-+ return count;
-+}
-+
-+APR_DECLARE(void *) apr_skiplist_find_compare(apr_skiplist *sli, void *data,
-+ apr_skiplistnode **iter,
-+ apr_skiplist_compare comp)
-+{
-+ apr_skiplistnode *m;
-+ apr_skiplist *sl;
-+ if (!comp) {
-+ if (iter) {
-+ *iter = NULL;
-+ }
-+ return NULL;
-+ }
-+ if (comp == sli->compare || !sli->index) {
-+ sl = sli;
-+ }
-+ else {
-+ apr_skiplist_find(sli->index, (void *)comp, &m);
-+ if (!m) {
-+ if (iter) {
-+ *iter = NULL;
-+ }
-+ return NULL;
-+ }
-+ sl = (apr_skiplist *) m->data;
-+ }
-+ skiplisti_find_compare(sl, data, &m, sl->comparek);
-+ if (iter) {
-+ *iter = m;
-+ }
-+ return (m) ? m->data : NULL;
-+}
-+
-+APR_DECLARE(void *) apr_skiplist_find(apr_skiplist *sl, void *data, apr_skiplistnode **iter)
-+{
-+ return apr_skiplist_find_compare(sl, data, iter, sl->compare);
-+}
-+
-+
-+APR_DECLARE(apr_skiplistnode *) apr_skiplist_getlist(apr_skiplist *sl)
-+{
-+ if (!sl->bottom) {
-+ return NULL;
-+ }
-+ return sl->bottom->next;
-+}
-+
-+APR_DECLARE(void *) apr_skiplist_next(apr_skiplist *sl, apr_skiplistnode **iter)
-+{
-+ if (!*iter) {
-+ return NULL;
-+ }
-+ *iter = (*iter)->next;
-+ return (*iter) ? ((*iter)->data) : NULL;
-+}
-+
-+APR_DECLARE(void *) apr_skiplist_previous(apr_skiplist *sl, apr_skiplistnode **iter)
-+{
-+ if (!*iter) {
-+ return NULL;
-+ }
-+ *iter = (*iter)->prev;
-+ return (*iter) ? ((*iter)->data) : NULL;
-+}
-+
-+static APR_INLINE int skiplist_height(const apr_skiplist *sl)
-+{
-+ /* Skiplists (even empty) always have a top node, although this
-+ * implementation defers its creation until the first insert, or
-+ * deletes it with the last remove. We want the real height here.
-+ */
-+ return sl->height ? sl->height : 1;
-+}
-+
-+APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert_compare(apr_skiplist *sl, void *data,
-+ apr_skiplist_compare comp)
-+{
-+ apr_skiplistnode *m, *p, *tmp, *ret = NULL;
-+ int ch, nh = 1;
-+
-+ if (!comp) {
-+ return NULL;
-+ }
-+
-+ ch = skiplist_height(sl);
-+ if (sl->preheight) {
-+ while (nh < sl->preheight && get_b_rand()) {
-+ nh++;
-+ }
-+ }
-+ else {
-+ while (nh <= ch && get_b_rand()) {
-+ nh++;
-+ }
-+ }
-+
-+ /* Now we have in nh the height at which we wish to insert our new node,
-+ * and in ch the current height: don't create skip paths to the inserted
-+ * element until the walk down through the tree (which decrements ch)
-+ * reaches nh. From there, any walk down pushes the current node on a
-+ * stack (the node(s) after which we would insert) to pop back through
-+ * for insertion later.
-+ */
-+ m = sl->top;
-+ while (m) {
-+ if (m->next) {
-+ int compared = comp(data, m->next->data);
-+ if (compared == 0) {
-+ /* Keep the existing element(s) */
-+ skiplist_qclear(&sl->stack_q);
-+ return NULL;
-+ }
-+ if (compared > 0) {
-+ m = m->next;
-+ continue;
-+ }
-+ }
-+ if (ch <= nh) {
-+ /* push on stack */
-+ skiplist_qpush(&sl->stack_q, m);
-+ }
-+ m = m->down;
-+ ch--;
-+ }
-+ /* Pop the stack and insert nodes */
-+ p = NULL;
-+ while ((m = skiplist_qpop(&sl->stack_q))) {
-+ tmp = skiplist_new_node(sl);
-+ tmp->next = m->next;
-+ if (m->next) {
-+ m->next->prev = tmp;
-+ }
-+ m->next = tmp;
-+ tmp->prev = m;
-+ tmp->up = NULL;
-+ tmp->nextindex = tmp->previndex = NULL;
-+ tmp->down = p;
-+ if (p) {
-+ p->up = tmp;
-+ }
-+ else {
-+ /* This sets ret to the bottom-most node we are inserting */
-+ ret = tmp;
-+ }
-+ tmp->data = data;
-+ tmp->sl = sl;
-+ p = tmp;
-+ }
-+
-+ /* Now we are sure the node is inserted, grow our tree to 'nh' tall */
-+ for (; sl->height < nh; sl->height++) {
-+ m = skiplist_new_node(sl);
-+ tmp = skiplist_new_node(sl);
-+ m->up = m->prev = m->nextindex = m->previndex = NULL;
-+ m->next = tmp;
-+ m->down = sl->top;
-+ m->data = NULL;
-+ m->sl = sl;
-+ if (sl->top) {
-+ sl->top->up = m;
-+ }
-+ else {
-+ sl->bottom = sl->bottomend = m;
-+ }
-+ sl->top = sl->topend = tmp->prev = m;
-+ tmp->up = tmp->next = tmp->nextindex = tmp->previndex = NULL;
-+ tmp->down = p;
-+ tmp->data = data;
-+ tmp->sl = sl;
-+ if (p) {
-+ p->up = tmp;
-+ }
-+ else {
-+ /* This sets ret to the bottom-most node we are inserting */
-+ ret = tmp;
-+ }
-+ p = tmp;
-+ }
-+ if (sl->index != NULL) {
-+ /*
-+ * this is a external insertion, we must insert into each index as
-+ * well
-+ */
-+ apr_skiplistnode *ni, *li;
-+ li = ret;
-+ for (p = apr_skiplist_getlist(sl->index); p; apr_skiplist_next(sl->index, &p)) {
-+ apr_skiplist *sli = (apr_skiplist *)p->data;
-+ ni = apr_skiplist_insert_compare(sli, ret->data, sli->compare);
-+ li->nextindex = ni;
-+ ni->previndex = li;
-+ li = ni;
-+ }
-+ }
-+ sl->size++;
-+ return ret;
-+}
-+
-+APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert(apr_skiplist *sl, void *data)
-+{
-+ return apr_skiplist_insert_compare(sl, data, sl->compare);
-+}
-+
-+#if 0
-+void skiplist_print_struct(apr_skiplist * sl, char *prefix)
-+{
-+ apr_skiplistnode *p, *q;
-+ fprintf(stderr, "Skiplist Structure (height: %d)\n", sl->height);
-+ p = sl->bottom;
-+ while (p) {
-+ q = p;
-+ fprintf(stderr, prefix);
-+ while (q) {
-+ fprintf(stderr, "%p ", q->data);
-+ q = q->up;
-+ }
-+ fprintf(stderr, "\n");
-+ p = p->next;
-+ }
-+}
-+#endif
-+
-+static int skiplisti_remove(apr_skiplist *sl, apr_skiplistnode *m, apr_skiplist_freefunc myfree)
-+{
-+ apr_skiplistnode *p;
-+ if (!m) {
-+ return 0;
-+ }
-+ if (m->nextindex) {
-+ skiplisti_remove(m->nextindex->sl, m->nextindex, NULL);
-+ }
-+ while (m->up) {
-+ m = m->up;
-+ }
-+ while (m) {
-+ p = m;
-+ p->prev->next = p->next;/* take me out of the list */
-+ if (p->next) {
-+ p->next->prev = p->prev; /* take me out of the list */
-+ }
-+ m = m->down;
-+ /* This only frees the actual data in the bottom one */
-+ if (!m && myfree && p->data) {
-+ myfree(p->data);
-+ }
-+ skiplist_free_node(sl, p);
-+ }
-+ sl->size--;
-+ while (sl->top && sl->top->next == NULL) {
-+ /* While the row is empty and we are not on the bottom row */
-+ p = sl->top;
-+ sl->top = sl->top->down;/* Move top down one */
-+ if (sl->top) {
-+ sl->top->up = NULL; /* Make it think its the top */
-+ }
-+ skiplist_free_node(sl, p);
-+ sl->height--;
-+ }
-+ if (!sl->top) {
-+ sl->bottom = sl->bottomend = NULL;
-+ sl->topend = NULL;
-+ }
-+ return skiplist_height(sl);
-+}
-+
-+APR_DECLARE(int) apr_skiplist_remove_compare(apr_skiplist *sli,
-+ void *data,
-+ apr_skiplist_freefunc myfree, apr_skiplist_compare comp)
-+{
-+ apr_skiplistnode *m;
-+ apr_skiplist *sl;
-+ if (!comp) {
-+ return 0;
-+ }
-+ if (comp == sli->comparek || !sli->index) {
-+ sl = sli;
-+ }
-+ else {
-+ apr_skiplist_find(sli->index, (void *)comp, &m);
-+ if (!m) {
-+ return 0;
-+ }
-+ sl = (apr_skiplist *) m->data;
-+ }
-+ skiplisti_find_compare(sl, data, &m, comp);
-+ if (!m) {
-+ return 0;
-+ }
-+ while (m->previndex) {
-+ m = m->previndex;
-+ }
-+ return skiplisti_remove(sl, m, myfree);
-+}
-+
-+APR_DECLARE(int) apr_skiplist_remove(apr_skiplist *sl, void *data, apr_skiplist_freefunc myfree)
-+{
-+ return apr_skiplist_remove_compare(sl, data, myfree, sl->comparek);
-+}
-+
-+APR_DECLARE(void) apr_skiplist_remove_all(apr_skiplist *sl, apr_skiplist_freefunc myfree)
-+{
-+ /*
-+ * This must remove even the place holder nodes (bottom though top)
-+ * because we specify in the API that one can free the Skiplist after
-+ * making this call without memory leaks
-+ */
-+ apr_skiplistnode *m, *p, *u;
-+ m = sl->bottom;
-+ while (m) {
-+ p = m->next;
-+ if (myfree && p && p->data) {
-+ myfree(p->data);
-+ }
-+ do {
-+ u = m->up;
-+ skiplist_free_node(sl, m);
-+ m = u;
-+ } while (m);
-+ m = p;
-+ }
-+ sl->top = sl->bottom = NULL;
-+ sl->topend = sl->bottomend = NULL;
-+ sl->height = 0;
-+ sl->size = 0;
-+}
-+
-+APR_DECLARE(void *) apr_skiplist_pop(apr_skiplist *a, apr_skiplist_freefunc myfree)
-+{
-+ apr_skiplistnode *sln;
-+ void *data = NULL;
-+ sln = apr_skiplist_getlist(a);
-+ if (sln) {
-+ data = sln->data;
-+ skiplisti_remove(a, sln, myfree);
-+ }
-+ return data;
-+}
-+
-+APR_DECLARE(void *) apr_skiplist_peek(apr_skiplist *a)
-+{
-+ apr_skiplistnode *sln;
-+ sln = apr_skiplist_getlist(a);
-+ if (sln) {
-+ return sln->data;
-+ }
-+ return NULL;
-+}
-+
-+static void skiplisti_destroy(void *vsl)
-+{
-+ apr_skiplist_destroy(vsl, NULL);
-+}
-+
-+APR_DECLARE(void) apr_skiplist_destroy(apr_skiplist *sl, apr_skiplist_freefunc myfree)
-+{
-+ while (apr_skiplist_pop(sl->index, skiplisti_destroy) != NULL)
-+ ;
-+ apr_skiplist_remove_all(sl, myfree);
-+ if (!sl->pool) {
-+ while (sl->nodes_q.pos)
-+ free(sl->nodes_q.data[--sl->nodes_q.pos]);
-+ free(sl->nodes_q.data);
-+ free(sl->stack_q.data);
-+ free(sl);
-+ }
-+}
-+
-+APR_DECLARE(apr_skiplist *) apr_skiplist_merge(apr_skiplist *sl1, apr_skiplist *sl2)
-+{
-+ /* Check integrity! */
-+ apr_skiplist temp;
-+ struct apr_skiplistnode *b2;
-+ if (sl1->bottomend == NULL || sl1->bottomend->prev == NULL) {
-+ apr_skiplist_remove_all(sl1, NULL);
-+ temp = *sl1;
-+ *sl1 = *sl2;
-+ *sl2 = temp;
-+ /* swap them so that sl2 can be freed normally upon return. */
-+ return sl1;
-+ }
-+ if(sl2->bottom == NULL || sl2->bottom->next == NULL) {
-+ apr_skiplist_remove_all(sl2, NULL);
-+ return sl1;
-+ }
-+ /* This is what makes it brute force... Just insert :/ */
-+ b2 = apr_skiplist_getlist(sl2);
-+ while (b2) {
-+ apr_skiplist_insert(sl1, b2->data);
-+ apr_skiplist_next(sl2, &b2);
-+ }
-+ apr_skiplist_remove_all(sl2, NULL);
-+ return sl1;
-+}
-diff --git a/server/mpm/event/apr_skiplist.h b/server/mpm/event/apr_skiplist.h
-new file mode 100644
-index 0000000..f56ff22
---- /dev/null
-+++ b/server/mpm/event/apr_skiplist.h
-@@ -0,0 +1,263 @@
-+/* Licensed to the Apache Software Foundation (ASF) under one or more
-+ * contributor license agreements. See the NOTICE file distributed with
-+ * this work for additional information regarding copyright ownership.
-+ * The ASF licenses this file to You 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 APR_SKIPLIST_H
-+#define APR_SKIPLIST_H
-+/**
-+ * @file apr_skiplist.h
-+ * @brief APR skip list implementation
-+ */
-+
-+#include "apr.h"
-+#include "apr_portable.h"
-+#include The suEXEC wrapper will write log information
- to the file defined with the
\n");
- return HTTP_FORBIDDEN;
- }
-+ }
- }
- #endif
- modssl_set_app_data2(ssl, r);
-diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h
-index 75fc0e3..31dbfa9 100644
---- a/modules/ssl/ssl_private.h
-+++ b/modules/ssl/ssl_private.h
-@@ -554,6 +554,7 @@ typedef struct {
- apr_global_mutex_t *stapling_cache_mutex;
- apr_global_mutex_t *stapling_refresh_mutex;
- #endif
-+ BOOL sni_required;
- } SSLModConfigRec;
-
- /** Structure representing configured filenames for certs and keys for
-@@ -786,7 +787,7 @@
- apr_status_t ssl_init_Engine(server_rec *, apr_pool_t *);
- apr_status_t ssl_init_ConfigureServer(server_rec *, apr_pool_t *, apr_pool_t *, SSLSrvConfigRec *,
- apr_array_header_t *);
--apr_status_t ssl_init_CheckServers(server_rec *, apr_pool_t *);
-+apr_status_t ssl_init_CheckServers(SSLModConfigRec *mc, server_rec *, apr_pool_t *);
- STACK_OF(X509_NAME)
- *ssl_init_FindCAList(server_rec *, apr_pool_t *, const char *, const char *);
- void ssl_init_Child(apr_pool_t *, server_rec *);
diff --git a/SOURCES/httpd-2.4.33-mddefault.patch b/SOURCES/httpd-2.4.33-mddefault.patch
new file mode 100644
index 0000000..9e82fb8
--- /dev/null
+++ b/SOURCES/httpd-2.4.33-mddefault.patch
@@ -0,0 +1,21 @@
+
+Override default.
+
+--- httpd-2.4.33/modules/md/mod_md_config.c.mddefault
++++ httpd-2.4.33/modules/md/mod_md_config.c
+@@ -54,10 +54,14 @@
+
+ #define DEF_VAL (-1)
+
++#ifndef MD_DEFAULT_STORE_DIR
++#define MD_DEFAULT_STORE_DIR "state/md"
++#endif
++
+ /* Default settings for the global conf */
+ static md_mod_conf_t defmc = {
+ NULL,
+- "md",
++ MD_DEFAULT_STORE_DIR,
+ NULL,
+ NULL,
+ 80,
diff --git a/SOURCES/httpd-2.4.34-CVE-2018-11763.patch b/SOURCES/httpd-2.4.34-CVE-2018-11763.patch
new file mode 100644
index 0000000..60bfcdc
--- /dev/null
+++ b/SOURCES/httpd-2.4.34-CVE-2018-11763.patch
@@ -0,0 +1,462 @@
+diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c
+index 805d677..a1b31d2 100644
+--- a/modules/http2/h2_session.c
++++ b/modules/http2/h2_session.c
+@@ -235,6 +235,7 @@ static int on_data_chunk_recv_cb(nghttp2_session *ngh2, uint8_t flags,
+ stream = h2_session_stream_get(session, stream_id);
+ if (stream) {
+ status = h2_stream_recv_DATA(stream, flags, data, len);
++ dispatch_event(session, H2_SESSION_EV_STREAM_CHANGE, 0, "stream data rcvd");
+ }
+ else {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03064)
+@@ -317,9 +318,9 @@ static int on_header_cb(nghttp2_session *ngh2, const nghttp2_frame *frame,
+ }
+
+ /**
+- * nghttp2 session has received a complete frame. Most, it uses
+- * for processing of internal state. HEADER and DATA frames however
+- * we need to handle ourself.
++ * nghttp2 session has received a complete frame. Most are used by nghttp2
++ * for processing of internal state. Some, like HEADER and DATA frames,
++ * we need to act on.
+ */
+ static int on_frame_recv_cb(nghttp2_session *ng2s,
+ const nghttp2_frame *frame,
+@@ -378,6 +379,9 @@ static int on_frame_recv_cb(nghttp2_session *ng2s,
+ "h2_stream(%ld-%d): WINDOW_UPDATE incr=%d",
+ session->id, (int)frame->hd.stream_id,
+ frame->window_update.window_size_increment);
++ if (nghttp2_session_want_write(session->ngh2)) {
++ dispatch_event(session, H2_SESSION_EV_FRAME_RCVD, 0, "window update");
++ }
+ break;
+ case NGHTTP2_RST_STREAM:
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03067)
+@@ -404,6 +408,12 @@ static int on_frame_recv_cb(nghttp2_session *ng2s,
+ frame->goaway.error_code, NULL);
+ }
+ break;
++ case NGHTTP2_SETTINGS:
++ if (APLOGctrace2(session->c)) {
++ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
++ H2_SSSN_MSG(session, "SETTINGS, len=%ld"), (long)frame->hd.length);
++ }
++ break;
+ default:
+ if (APLOGctrace2(session->c)) {
+ char buffer[256];
+@@ -415,7 +425,40 @@ static int on_frame_recv_cb(nghttp2_session *ng2s,
+ }
+ break;
+ }
+- return (APR_SUCCESS == rv)? 0 : NGHTTP2_ERR_PROTO;
++
++ if (session->state == H2_SESSION_ST_IDLE) {
++ /* We received a frame, but session is in state IDLE. That means the frame
++ * did not really progress any of the (possibly) open streams. It was a meta
++ * frame, e.g. SETTINGS/WINDOW_UPDATE/unknown/etc.
++ * Remember: IDLE means we cannot send because either there are no streams open or
++ * all open streams are blocked on exhausted WINDOWs for outgoing data.
++ * The more frames we receive that do not change this, the less interested we
++ * become in serving this connection. This is expressed in increasing "idle_delays".
++ * Eventually, the connection will timeout and we'll close it. */
++ session->idle_frames = H2MIN(session->idle_frames + 1, session->frames_received);
++ ap_log_cerror( APLOG_MARK, APLOG_TRACE2, 0, session->c,
++ H2_SSSN_MSG(session, "session has %ld idle frames"),
++ (long)session->idle_frames);
++ if (session->idle_frames > 10) {
++ apr_size_t busy_frames = H2MAX(session->frames_received - session->idle_frames, 1);
++ int idle_ratio = (int)(session->idle_frames / busy_frames);
++ if (idle_ratio > 100) {
++ session->idle_delay = apr_time_from_msec(H2MIN(1000, idle_ratio));
++ }
++ else if (idle_ratio > 10) {
++ session->idle_delay = apr_time_from_msec(10);
++ }
++ else if (idle_ratio > 1) {
++ session->idle_delay = apr_time_from_msec(1);
++ }
++ else {
++ session->idle_delay = 0;
++ }
++ }
++ }
++
++ if (APR_SUCCESS != rv) return NGHTTP2_ERR_PROTO;
++ return 0;
+ }
+
+ static int h2_session_continue_data(h2_session *session) {
+@@ -1603,23 +1646,57 @@ static void update_child_status(h2_session *session, int status, const char *msg
+
+ static void transit(h2_session *session, const char *action, h2_session_state nstate)
+ {
++ apr_time_t timeout;
++ int ostate, loglvl;
++ const char *s;
++
+ if (session->state != nstate) {
+- int loglvl = APLOG_DEBUG;
+- if ((session->state == H2_SESSION_ST_BUSY && nstate == H2_SESSION_ST_WAIT)
+- || (session->state == H2_SESSION_ST_WAIT && nstate == H2_SESSION_ST_BUSY)){
++ ostate = session->state;
++ session->state = nstate;
++
++ loglvl = APLOG_DEBUG;
++ if ((ostate == H2_SESSION_ST_BUSY && nstate == H2_SESSION_ST_WAIT)
++ || (ostate == H2_SESSION_ST_WAIT && nstate == H2_SESSION_ST_BUSY)){
+ loglvl = APLOG_TRACE1;
+ }
+ ap_log_cerror(APLOG_MARK, loglvl, 0, session->c,
+ H2_SSSN_LOG(APLOGNO(03078), session,
+ "transit [%s] -- %s --> [%s]"),
+- h2_session_state_str(session->state), action,
++ h2_session_state_str(ostate), action,
+ h2_session_state_str(nstate));
+- session->state = nstate;
++
+ switch (session->state) {
+ case H2_SESSION_ST_IDLE:
+- update_child_status(session, (session->open_streams == 0?
+- SERVER_BUSY_KEEPALIVE
+- : SERVER_BUSY_READ), "idle");
++ if (!session->remote.emitted_count) {
++ /* on fresh connections, with async mpm, do not return
++ * to mpm for a second. This gives the first request a better
++ * chance to arrive (und connection leaving IDLE state).
++ * If we return to mpm right away, this connection has the
++ * same chance of being cleaned up by the mpm as connections
++ * that already served requests - not fair. */
++ session->idle_sync_until = apr_time_now() + apr_time_from_sec(1);
++ s = "timeout";
++ timeout = H2MAX(session->s->timeout, session->s->keep_alive_timeout);
++ update_child_status(session, SERVER_BUSY_READ, "idle");
++ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
++ H2_SSSN_LOG("", session, "enter idle, timeout = %d sec"),
++ (int)apr_time_sec(H2MAX(session->s->timeout, session->s->keep_alive_timeout)));
++ }
++ else if (session->open_streams) {
++ s = "timeout";
++ timeout = session->s->keep_alive_timeout;
++ update_child_status(session, SERVER_BUSY_KEEPALIVE, "idle");
++ }
++ else {
++ /* normal keepalive setup */
++ s = "keepalive";
++ timeout = session->s->keep_alive_timeout;
++ update_child_status(session, SERVER_BUSY_KEEPALIVE, "idle");
++ }
++ session->idle_until = apr_time_now() + timeout;
++ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
++ H2_SSSN_LOG("", session, "enter idle, %s = %d sec"),
++ s, (int)apr_time_sec(timeout));
+ break;
+ case H2_SESSION_ST_DONE:
+ update_child_status(session, SERVER_CLOSING, "done");
+@@ -1726,8 +1803,6 @@ static void h2_session_ev_no_io(h2_session *session, int arg, const char *msg)
+ * This means we only wait for WINDOW_UPDATE from the
+ * client and can block on READ. */
+ transit(session, "no io (flow wait)", H2_SESSION_ST_IDLE);
+- session->idle_until = apr_time_now() + session->s->timeout;
+- session->keep_sync_until = session->idle_until;
+ /* Make sure we have flushed all previously written output
+ * so that the client will react. */
+ if (h2_conn_io_flush(&session->io) != APR_SUCCESS) {
+@@ -1738,12 +1813,7 @@ static void h2_session_ev_no_io(h2_session *session, int arg, const char *msg)
+ }
+ else if (session->local.accepting) {
+ /* When we have no streams, but accept new, switch to idle */
+- apr_time_t now = apr_time_now();
+ transit(session, "no io (keepalive)", H2_SESSION_ST_IDLE);
+- session->idle_until = (session->remote.emitted_count?
+- session->s->keep_alive_timeout :
+- session->s->timeout) + now;
+- session->keep_sync_until = now + apr_time_from_sec(1);
+ }
+ else {
+ /* We are no longer accepting new streams and there are
+@@ -1758,12 +1828,25 @@ static void h2_session_ev_no_io(h2_session *session, int arg, const char *msg)
+ }
+ }
+
+-static void h2_session_ev_data_read(h2_session *session, int arg, const char *msg)
++static void h2_session_ev_frame_rcvd(h2_session *session, int arg, const char *msg)
++{
++ switch (session->state) {
++ case H2_SESSION_ST_IDLE:
++ case H2_SESSION_ST_WAIT:
++ transit(session, "frame received", H2_SESSION_ST_BUSY);
++ break;
++ default:
++ /* nop */
++ break;
++ }
++}
++
++static void h2_session_ev_stream_change(h2_session *session, int arg, const char *msg)
+ {
+ switch (session->state) {
+ case H2_SESSION_ST_IDLE:
+ case H2_SESSION_ST_WAIT:
+- transit(session, "data read", H2_SESSION_ST_BUSY);
++ transit(session, "stream change", H2_SESSION_ST_BUSY);
+ break;
+ default:
+ /* nop */
+@@ -1803,16 +1886,6 @@ static void h2_session_ev_pre_close(h2_session *session, int arg, const char *ms
+ static void ev_stream_open(h2_session *session, h2_stream *stream)
+ {
+ h2_iq_append(session->in_process, stream->id);
+- switch (session->state) {
+- case H2_SESSION_ST_IDLE:
+- if (session->open_streams == 1) {
+- /* enter timeout, since we have a stream again */
+- session->idle_until = (session->s->timeout + apr_time_now());
+- }
+- break;
+- default:
+- break;
+- }
+ }
+
+ static void ev_stream_closed(h2_session *session, h2_stream *stream)
+@@ -1825,11 +1898,6 @@ static void ev_stream_closed(h2_session *session, h2_stream *stream)
+ }
+ switch (session->state) {
+ case H2_SESSION_ST_IDLE:
+- if (session->open_streams == 0) {
+- /* enter keepalive timeout, since we no longer have streams */
+- session->idle_until = (session->s->keep_alive_timeout
+- + apr_time_now());
+- }
+ break;
+ default:
+ break;
+@@ -1887,6 +1955,7 @@ static void on_stream_state_enter(void *ctx, h2_stream *stream)
+ default:
+ break;
+ }
++ dispatch_event(session, H2_SESSION_EV_STREAM_CHANGE, 0, "stream state change");
+ }
+
+ static void on_stream_event(void *ctx, h2_stream *stream,
+@@ -1945,8 +2014,8 @@ static void dispatch_event(h2_session *session, h2_session_event_t ev,
+ case H2_SESSION_EV_NO_IO:
+ h2_session_ev_no_io(session, arg, msg);
+ break;
+- case H2_SESSION_EV_DATA_READ:
+- h2_session_ev_data_read(session, arg, msg);
++ case H2_SESSION_EV_FRAME_RCVD:
++ h2_session_ev_frame_rcvd(session, arg, msg);
+ break;
+ case H2_SESSION_EV_NGH2_DONE:
+ h2_session_ev_ngh2_done(session, arg, msg);
+@@ -1957,6 +2026,9 @@ static void dispatch_event(h2_session *session, h2_session_event_t ev,
+ case H2_SESSION_EV_PRE_CLOSE:
+ h2_session_ev_pre_close(session, arg, msg);
+ break;
++ case H2_SESSION_EV_STREAM_CHANGE:
++ h2_session_ev_stream_change(session, arg, msg);
++ break;
+ default:
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
+ H2_SSSN_MSG(session, "unknown event %d"), ev);
+@@ -1990,13 +2062,15 @@ apr_status_t h2_session_process(h2_session *session, int async)
+ apr_status_t status = APR_SUCCESS;
+ conn_rec *c = session->c;
+ int rv, mpm_state, trace = APLOGctrace3(c);
+-
++ apr_time_t now;
++
+ if (trace) {
+ ap_log_cerror( APLOG_MARK, APLOG_TRACE3, status, c,
+ H2_SSSN_MSG(session, "process start, async=%d"), async);
+ }
+
+ while (session->state != H2_SESSION_ST_DONE) {
++ now = apr_time_now();
+ session->have_read = session->have_written = 0;
+
+ if (session->local.accepting
+@@ -2034,39 +2108,42 @@ apr_status_t h2_session_process(h2_session *session, int async)
+ break;
+
+ case H2_SESSION_ST_IDLE:
+- /* We trust our connection into the default timeout/keepalive
+- * handling of the core filters/mpm iff:
+- * - keep_sync_until is not set
+- * - we have an async mpm
+- * - we have no open streams to process
+- * - we are not sitting on a Upgrade: request
+- * - we already have seen at least one request
+- */
+- if (!session->keep_sync_until && async && !session->open_streams
+- && !session->r && session->remote.emitted_count) {
++ if (session->idle_until && (apr_time_now() + session->idle_delay) > session->idle_until) {
++ ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
++ H2_SSSN_MSG(session, "idle, timeout reached, closing"));
++ if (session->idle_delay) {
++ apr_table_setn(session->c->notes, "short-lingering-close", "1");
++ }
++ dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, "timeout");
++ goto out;
++ }
++
++ if (session->idle_delay) {
++ /* we are less interested in spending time on this connection */
++ ap_log_cerror( APLOG_MARK, APLOG_TRACE2, status, c,
++ H2_SSSN_MSG(session, "session is idle (%ld ms), idle wait %ld sec left"),
++ (long)apr_time_as_msec(session->idle_delay),
++ (long)apr_time_sec(session->idle_until - now));
++ apr_sleep(session->idle_delay);
++ session->idle_delay = 0;
++ }
++
++ h2_conn_io_flush(&session->io);
++ if (async && !session->r && (now > session->idle_sync_until)) {
+ if (trace) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, c,
+ H2_SSSN_MSG(session,
+ "nonblock read, %d streams open"),
+ session->open_streams);
+ }
+- h2_conn_io_flush(&session->io);
+ status = h2_session_read(session, 0);
+
+ if (status == APR_SUCCESS) {
+ session->have_read = 1;
+- dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
+ }
+- else if (APR_STATUS_IS_EAGAIN(status)
+- || APR_STATUS_IS_TIMEUP(status)) {
+- if (apr_time_now() > session->idle_until) {
+- dispatch_event(session,
+- H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
+- }
+- else {
+- status = APR_EAGAIN;
+- goto out;
+- }
++ else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) {
++ status = APR_EAGAIN;
++ goto out;
+ }
+ else {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
+@@ -2078,7 +2155,6 @@ apr_status_t h2_session_process(h2_session *session, int async)
+ }
+ else {
+ /* make certain, we send everything before we idle */
+- h2_conn_io_flush(&session->io);
+ if (trace) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, c,
+ H2_SSSN_MSG(session,
+@@ -2090,7 +2166,6 @@ apr_status_t h2_session_process(h2_session *session, int async)
+ */
+ status = h2_mplx_idle(session->mplx);
+ if (status == APR_EAGAIN) {
+- dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
+ break;
+ }
+ else if (status != APR_SUCCESS) {
+@@ -2101,33 +2176,11 @@ apr_status_t h2_session_process(h2_session *session, int async)
+ status = h2_session_read(session, 1);
+ if (status == APR_SUCCESS) {
+ session->have_read = 1;
+- dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
+ }
+ else if (status == APR_EAGAIN) {
+ /* nothing to read */
+ }
+ else if (APR_STATUS_IS_TIMEUP(status)) {
+- apr_time_t now = apr_time_now();
+- if (now > session->keep_sync_until) {
+- /* if we are on an async mpm, now is the time that
+- * we may dare to pass control to it. */
+- session->keep_sync_until = 0;
+- }
+- if (now > session->idle_until) {
+- if (trace) {
+- ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, c,
+- H2_SSSN_MSG(session,
+- "keepalive timeout"));
+- }
+- dispatch_event(session,
+- H2_SESSION_EV_CONN_TIMEOUT, 0, "timeout");
+- }
+- else if (trace) {
+- ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, c,
+- H2_SSSN_MSG(session,
+- "keepalive, %f sec left"),
+- (session->idle_until - now) / 1000000.0f);
+- }
+ /* continue reading handling */
+ }
+ else if (APR_STATUS_IS_ECONNABORTED(status)
+@@ -2145,6 +2198,18 @@ apr_status_t h2_session_process(h2_session *session, int async)
+ dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, "error");
+ }
+ }
++ if (nghttp2_session_want_write(session->ngh2)) {
++ ap_update_child_status(session->c->sbh, SERVER_BUSY_WRITE, NULL);
++ status = h2_session_send(session);
++ if (status == APR_SUCCESS) {
++ status = h2_conn_io_flush(&session->io);
++ }
++ if (status != APR_SUCCESS) {
++ dispatch_event(session, H2_SESSION_EV_CONN_ERROR,
++ H2_ERR_INTERNAL_ERROR, "writing");
++ break;
++ }
++ }
+ break;
+
+ case H2_SESSION_ST_BUSY:
+@@ -2154,7 +2219,6 @@ apr_status_t h2_session_process(h2_session *session, int async)
+ status = h2_session_read(session, 0);
+ if (status == APR_SUCCESS) {
+ session->have_read = 1;
+- dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
+ }
+ else if (status == APR_EAGAIN) {
+ /* nothing to read */
+@@ -2218,7 +2282,7 @@ apr_status_t h2_session_process(h2_session *session, int async)
+ session->iowait);
+ if (status == APR_SUCCESS) {
+ session->wait_us = 0;
+- dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
++ dispatch_event(session, H2_SESSION_EV_STREAM_CHANGE, 0, NULL);
+ }
+ else if (APR_STATUS_IS_TIMEUP(status)) {
+ /* go back to checking all inputs again */
+diff --git a/modules/http2/h2_session.h b/modules/http2/h2_session.h
+index 486938b..452c182 100644
+--- a/modules/http2/h2_session.h
++++ b/modules/http2/h2_session.h
+@@ -66,10 +66,11 @@ typedef enum {
+ H2_SESSION_EV_PROTO_ERROR, /* protocol error */
+ H2_SESSION_EV_CONN_TIMEOUT, /* connection timeout */
+ H2_SESSION_EV_NO_IO, /* nothing has been read or written */
+- H2_SESSION_EV_DATA_READ, /* connection data has been read */
++ H2_SESSION_EV_FRAME_RCVD, /* a frame has been received */
+ H2_SESSION_EV_NGH2_DONE, /* nghttp2 wants neither read nor write anything */
+ H2_SESSION_EV_MPM_STOPPING, /* the process is stopping */
+ H2_SESSION_EV_PRE_CLOSE, /* connection will close after this */
++ H2_SESSION_EV_STREAM_CHANGE, /* a stream (state/input/output) changed */
+ } h2_session_event_t;
+
+ typedef struct h2_session {
+@@ -118,8 +119,10 @@ typedef struct h2_session {
+ apr_size_t max_stream_mem; /* max buffer memory for a single stream */
+
+ apr_time_t idle_until; /* Time we shut down due to sheer boredom */
+- apr_time_t keep_sync_until; /* Time we sync wait until passing to async mpm */
+-
++ apr_time_t idle_sync_until; /* Time we sync wait until keepalive handling kicks in */
++ apr_size_t idle_frames; /* number of rcvd frames that kept session in idle state */
++ apr_interval_time_t idle_delay; /* Time we delay processing rcvd frames in idle state */
++
+ apr_bucket_brigade *bbtmp; /* brigade for keeping temporary data */
+ struct apr_thread_cond_t *iowait; /* our cond when trywaiting for data */
+
diff --git a/SOURCES/httpd-2.4.34-apr-escape.patch b/SOURCES/httpd-2.4.34-apr-escape.patch
new file mode 100644
index 0000000..3d8a401
--- /dev/null
+++ b/SOURCES/httpd-2.4.34-apr-escape.patch
@@ -0,0 +1,24 @@
+diff --git a/modules/proxy/mod_proxy_balancer.c b/modules/proxy/mod_proxy_balancer.c
+index e0ccc31..c6c7e89 100644
+--- a/modules/proxy/mod_proxy_balancer.c
++++ b/modules/proxy/mod_proxy_balancer.c
+@@ -22,7 +22,7 @@
+ #include "apr_version.h"
+ #include "ap_hooks.h"
+ #include "apr_date.h"
+-#include "apr_escape.h"
++#include "util_md5.h"
+ #include "mod_watchdog.h"
+
+ static const char *balancer_mutex_type = "proxy-balancer-shm";
+@@ -774,9 +774,8 @@ static apr_status_t lock_remove(void *data)
+ }
+ }
+ }
+- apr_md5_final(md5, &md5_ctx);
+
+- return apr_pescape_hex(p, md5, sizeof md5, 0);
++ return ap_md5contextTo64(p, &md5_ctx);
+ }
+
+ /*
diff --git a/SOURCES/httpd-2.4.34-export.patch b/SOURCES/httpd-2.4.34-export.patch
new file mode 100644
index 0000000..18cdafa
--- /dev/null
+++ b/SOURCES/httpd-2.4.34-export.patch
@@ -0,0 +1,22 @@
+
+There is no need to "suck in" the apr/apr-util symbols when using
+a shared libapr{,util}, it just bloats the symbol table; so don't.
+
+Upstream-HEAD: needed
+Upstream-2.0: omit
+Upstream-Status: EXPORT_DIRS change is conditional on using shared apr
+
+diff --git a/server/Makefile.in b/server/Makefile.in
+index 1fa3344..f635d76 100644
+--- a/server/Makefile.in
++++ b/server/Makefile.in
+@@ -60,9 +60,6 @@ export_files:
+ ls $$dir/*.h ; \
+ done; \
+ echo "$(top_srcdir)/server/mpm_fdqueue.h"; \
+- for dir in $(EXPORT_DIRS_APR); do \
+- ls $$dir/ap[ru].h $$dir/ap[ru]_*.h 2>/dev/null; \
+- done; \
+ ) | sed -e s,//,/,g | sort -u > $@
+
+ exports.c: export_files
diff --git a/SOURCES/httpd-2.4.34-r1836472.patch b/SOURCES/httpd-2.4.34-r1836472.patch
new file mode 100644
index 0000000..ff4e1c2
--- /dev/null
+++ b/SOURCES/httpd-2.4.34-r1836472.patch
@@ -0,0 +1,11 @@
+--- a/modules/ssl/ssl_engine_ocsp.c 2018/07/23 08:47:59 1836471
++++ b/modules/ssl/ssl_engine_ocsp.c 2018/07/23 08:54:29 1836472
+@@ -61,7 +61,7 @@
+ /* Use default responder URL if forced by configuration, else use
+ * certificate-specified responder, falling back to default if
+ * necessary and possible. */
+- if (sc->server->ocsp_force_default) {
++ if (sc->server->ocsp_force_default == TRUE) {
+ s = sc->server->ocsp_responder;
+ }
+ else {
diff --git a/SOURCES/httpd-2.4.34-scl-libcurl.patch b/SOURCES/httpd-2.4.34-scl-libcurl.patch
new file mode 100644
index 0000000..11f6f88
--- /dev/null
+++ b/SOURCES/httpd-2.4.34-scl-libcurl.patch
@@ -0,0 +1,40 @@
+diff --git a/modules/md/config2.m4 b/modules/md/config2.m4
+index f154107..6a57dd4 100644
+--- a/modules/md/config2.m4
++++ b/modules/md/config2.m4
+@@ -52,12 +52,12 @@ AC_DEFUN([APACHE_CHECK_CURL],[
+ saved_PKG_CONFIG_PATH="$PKG_CONFIG_PATH"
+ AC_MSG_CHECKING([for pkg-config along $PKG_CONFIG_PATH])
+ if test "x$ap_curl_base" != "x" ; then
+- if test -f "${ap_curl_base}/lib/pkgconfig/libcurl.pc"; then
++ if test -f "${ap_curl_base}/lib/pkgconfig/httpd24-libcurl.pc"; then
+ dnl Ensure that the given path is used by pkg-config too, otherwise
+ dnl the system libcurl.pc might be picked up instead.
+ PKG_CONFIG_PATH="${ap_curl_base}/lib/pkgconfig${PKG_CONFIG_PATH+:}${PKG_CONFIG_PATH}"
+ export PKG_CONFIG_PATH
+- elif test -f "${ap_curl_base}/lib64/pkgconfig/libcurl.pc"; then
++ elif test -f "${ap_curl_base}/lib64/pkgconfig/httpd24-libcurl.pc"; then
+ dnl Ensure that the given path is used by pkg-config too, otherwise
+ dnl the system libcurl.pc might be picked up instead.
+ PKG_CONFIG_PATH="${ap_curl_base}/lib64/pkgconfig${PKG_CONFIG_PATH+:}${PKG_CONFIG_PATH}"
+@@ -69,16 +69,16 @@ AC_DEFUN([APACHE_CHECK_CURL],[
+ PKGCONFIG_LIBOPTS="--static"
+ fi
+ ])
+- ap_curl_libs="`$PKGCONFIG $PKGCONFIG_LIBOPTS --libs-only-l --silence-errors libcurl`"
++ ap_curl_libs="`$PKGCONFIG $PKGCONFIG_LIBOPTS --libs-only-l --silence-errors httpd24-libcurl`"
+ if test $? -eq 0; then
+ ap_curl_found="yes"
+- pkglookup="`$PKGCONFIG --cflags-only-I libcurl`"
++ pkglookup="`$PKGCONFIG --cflags-only-I httpd24-libcurl`"
+ APR_ADDTO(CPPFLAGS, [$pkglookup])
+ APR_ADDTO(MOD_CFLAGS, [$pkglookup])
+- pkglookup="`$PKGCONFIG $PKGCONFIG_LIBOPTS --libs-only-L libcurl`"
++ pkglookup="`$PKGCONFIG $PKGCONFIG_LIBOPTS --libs-only-L httpd24-libcurl`"
+ APR_ADDTO(LDFLAGS, [$pkglookup])
+ APR_ADDTO(MOD_LDFLAGS, [$pkglookup])
+- pkglookup="`$PKGCONFIG $PKGCONFIG_LIBOPTS --libs-only-other libcurl`"
++ pkglookup="`$PKGCONFIG $PKGCONFIG_LIBOPTS --libs-only-other httpd24-libcurl`"
+ APR_ADDTO(LDFLAGS, [$pkglookup])
+ APR_ADDTO(MOD_LDFLAGS, [$pkglookup])
+ fi
diff --git a/SOURCES/httpd-2.4.34-scl-libnghttp2.patch b/SOURCES/httpd-2.4.34-scl-libnghttp2.patch
new file mode 100644
index 0000000..8fad28e
--- /dev/null
+++ b/SOURCES/httpd-2.4.34-scl-libnghttp2.patch
@@ -0,0 +1,40 @@
+diff --git a/modules/http2/config2.m4 b/modules/http2/config2.m4
+index e8cefe3..904e519 100644
+--- a/modules/http2/config2.m4
++++ b/modules/http2/config2.m4
+@@ -81,12 +81,12 @@ AC_DEFUN([APACHE_CHECK_NGHTTP2],[
+ saved_PKG_CONFIG_PATH="$PKG_CONFIG_PATH"
+ AC_MSG_CHECKING([for pkg-config along $PKG_CONFIG_PATH])
+ if test "x$ap_nghttp2_base" != "x" ; then
+- if test -f "${ap_nghttp2_base}/lib/pkgconfig/libnghttp2.pc"; then
++ if test -f "${ap_nghttp2_base}/lib/pkgconfig/httpd24-libnghttp2.pc"; then
+ dnl Ensure that the given path is used by pkg-config too, otherwise
+ dnl the system libnghttp2.pc might be picked up instead.
+ PKG_CONFIG_PATH="${ap_nghttp2_base}/lib/pkgconfig${PKG_CONFIG_PATH+:}${PKG_CONFIG_PATH}"
+ export PKG_CONFIG_PATH
+- elif test -f "${ap_nghttp2_base}/lib64/pkgconfig/libnghttp2.pc"; then
++ elif test -f "${ap_nghttp2_base}/lib64/pkgconfig/httpd24-libnghttp2.pc"; then
+ dnl Ensure that the given path is used by pkg-config too, otherwise
+ dnl the system libnghttp2.pc might be picked up instead.
+ PKG_CONFIG_PATH="${ap_nghttp2_base}/lib64/pkgconfig${PKG_CONFIG_PATH+:}${PKG_CONFIG_PATH}"
+@@ -98,16 +98,16 @@ AC_DEFUN([APACHE_CHECK_NGHTTP2],[
+ PKGCONFIG_LIBOPTS="--static"
+ fi
+ ])
+- ap_nghttp2_libs="`$PKGCONFIG $PKGCONFIG_LIBOPTS --libs-only-l --silence-errors libnghttp2`"
++ ap_nghttp2_libs="`$PKGCONFIG $PKGCONFIG_LIBOPTS --libs-only-l --silence-errors httpd24-libnghttp2`"
+ if test $? -eq 0; then
+ ap_nghttp2_found="yes"
+- pkglookup="`$PKGCONFIG --cflags-only-I libnghttp2`"
++ pkglookup="`$PKGCONFIG --cflags-only-I httpd24-libnghttp2`"
+ APR_ADDTO(CPPFLAGS, [$pkglookup])
+ APR_ADDTO(MOD_CFLAGS, [$pkglookup])
+- pkglookup="`$PKGCONFIG $PKGCONFIG_LIBOPTS --libs-only-L libnghttp2`"
++ pkglookup="`$PKGCONFIG $PKGCONFIG_LIBOPTS --libs-only-L httpd24-libnghttp2`"
+ APR_ADDTO(LDFLAGS, [$pkglookup])
+ APR_ADDTO(MOD_LDFLAGS, [$pkglookup])
+- pkglookup="`$PKGCONFIG $PKGCONFIG_LIBOPTS --libs-only-other libnghttp2`"
++ pkglookup="`$PKGCONFIG $PKGCONFIG_LIBOPTS --libs-only-other httpd24-libnghttp2`"
+ APR_ADDTO(LDFLAGS, [$pkglookup])
+ APR_ADDTO(MOD_LDFLAGS, [$pkglookup])
+ fi
diff --git a/SOURCES/httpd-2.4.34-skiplist.patch b/SOURCES/httpd-2.4.34-skiplist.patch
new file mode 100644
index 0000000..06e3efa
--- /dev/null
+++ b/SOURCES/httpd-2.4.34-skiplist.patch
@@ -0,0 +1,1021 @@
+diff --git a/server/mpm/event/config.m4 b/server/mpm/event/config.m4
+index c891c75..351f1ac 100644
+--- a/server/mpm/event/config.m4
++++ b/server/mpm/event/config.m4
+@@ -7,8 +7,6 @@ elif test $have_threaded_sig_graceful != yes; then
+ AC_MSG_RESULT(no - SIG_GRACEFUL cannot be used with a threaded MPM)
+ elif test $ac_cv_have_threadsafe_pollset != yes; then
+ AC_MSG_RESULT(no - APR_POLLSET_THREADSAFE is not supported)
+-elif test $apr_has_skiplist != yes; then
+- AC_MSG_RESULT(no - APR skiplist is not available, need APR 1.5.x or later)
+ else
+ AC_MSG_RESULT(yes)
+ APACHE_MPM_SUPPORTED(event, yes, yes)
+diff --git a/server/mpm/event/config3.m4 b/server/mpm/event/config3.m4
+index 09d3626..722dfcb 100644
+--- a/server/mpm/event/config3.m4
++++ b/server/mpm/event/config3.m4
+@@ -2,6 +2,6 @@ dnl ## XXX - Need a more thorough check of the proper flags to use
+
+ APACHE_SUBST(MOD_MPM_EVENT_LDADD)
+
+-APACHE_MPM_MODULE(event, $enable_mpm_event, event.lo,[
++APACHE_MPM_MODULE(event, $enable_mpm_event, event.lo apr_skiplist.lo, [
+ AC_CHECK_FUNCS(pthread_kill)
+ ], , [\$(MOD_MPM_EVENT_LDADD)])
+diff --git a/server/mpm/event/apr_skiplist.c b/server/mpm/event/apr_skiplist.c
+new file mode 100644
+index 0000000..b4696bd
+--- /dev/null
++++ b/server/mpm/event/apr_skiplist.c
+@@ -0,0 +1,721 @@
++/* Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.
++ */
++
++/*
++ * Modified to use APR and APR pools.
++ * TODO: Is malloc() better? Will long running skiplists grow too much?
++ * Keep the skiplist_alloc() and skiplist_free() until we know
++ * Yeah, if using pools it means some bogus cycles for checks
++ * (and an useless function call for skiplist_free) which we
++ * can removed if/when needed.
++ */
++
++#include "apr_skiplist.h"
++
++typedef struct {
++ apr_skiplistnode **data;
++ size_t size, pos;
++ apr_pool_t *p;
++} apr_skiplist_q;
++
++struct apr_skiplist {
++ apr_skiplist_compare compare;
++ apr_skiplist_compare comparek;
++ int height;
++ int preheight;
++ size_t size;
++ apr_skiplistnode *top;
++ apr_skiplistnode *bottom;
++ /* These two are needed for appending */
++ apr_skiplistnode *topend;
++ apr_skiplistnode *bottomend;
++ apr_skiplist *index;
++ apr_array_header_t *memlist;
++ apr_skiplist_q nodes_q,
++ stack_q;
++ apr_pool_t *pool;
++};
++
++struct apr_skiplistnode {
++ void *data;
++ apr_skiplistnode *next;
++ apr_skiplistnode *prev;
++ apr_skiplistnode *down;
++ apr_skiplistnode *up;
++ apr_skiplistnode *previndex;
++ apr_skiplistnode *nextindex;
++ apr_skiplist *sl;
++};
++
++static int get_b_rand(void)
++{
++ static int ph = 32; /* More bits than we will ever use */
++ static int randseq;
++ if (ph > 31) { /* Num bits in return of rand() */
++ ph = 0;
++ randseq = rand();
++ }
++ return randseq & (1 << ph++);
++}
++
++typedef struct {
++ size_t size;
++ apr_array_header_t *list;
++} memlist_t;
++
++typedef struct {
++ void *ptr;
++ char inuse;
++} chunk_t;
++
++APR_DECLARE(void *) apr_skiplist_alloc(apr_skiplist *sl, size_t size)
++{
++ if (sl->pool) {
++ void *ptr;
++ int found_size = 0;
++ int i;
++ chunk_t *newchunk;
++ memlist_t *memlist = (memlist_t *)sl->memlist->elts;
++ for (i = 0; i < sl->memlist->nelts; i++) {
++ if (memlist->size == size) {
++ int j;
++ chunk_t *chunk = (chunk_t *)memlist->list->elts;
++ found_size = 1;
++ for (j = 0; j < memlist->list->nelts; j++) {
++ if (!chunk->inuse) {
++ chunk->inuse = 1;
++ return chunk->ptr;
++ }
++ chunk++;
++ }
++ break; /* no free of this size; punt */
++ }
++ memlist++;
++ }
++ /* no free chunks */
++ ptr = apr_palloc(sl->pool, size);
++ if (!ptr) {
++ return ptr;
++ }
++ /*
++ * is this a new sized chunk? If so, we need to create a new
++ * array of them. Otherwise, re-use what we already have.
++ */
++ if (!found_size) {
++ memlist = apr_array_push(sl->memlist);
++ memlist->size = size;
++ memlist->list = apr_array_make(sl->pool, 20, sizeof(chunk_t));
++ }
++ newchunk = apr_array_push(memlist->list);
++ newchunk->ptr = ptr;
++ newchunk->inuse = 1;
++ return ptr;
++ }
++ else {
++ return malloc(size);
++ }
++}
++
++APR_DECLARE(void) apr_skiplist_free(apr_skiplist *sl, void *mem)
++{
++ if (!sl->pool) {
++ free(mem);
++ }
++ else {
++ int i;
++ memlist_t *memlist = (memlist_t *)sl->memlist->elts;
++ for (i = 0; i < sl->memlist->nelts; i++) {
++ int j;
++ chunk_t *chunk = (chunk_t *)memlist->list->elts;
++ for (j = 0; j < memlist->list->nelts; j++) {
++ if (chunk->ptr == mem) {
++ chunk->inuse = 0;
++ return;
++ }
++ chunk++;
++ }
++ memlist++;
++ }
++ }
++}
++
++static apr_status_t skiplist_qpush(apr_skiplist_q *q, apr_skiplistnode *m)
++{
++ if (q->pos >= q->size) {
++ apr_skiplistnode **data;
++ size_t size = (q->pos) ? q->pos * 2 : 32;
++ if (q->p) {
++ data = apr_palloc(q->p, size * sizeof(*data));
++ if (data) {
++ memcpy(data, q->data, q->pos * sizeof(*data));
++ }
++ }
++ else {
++ data = realloc(q->data, size * sizeof(*data));
++ }
++ if (!data) {
++ return APR_ENOMEM;
++ }
++ q->data = data;
++ q->size = size;
++ }
++ q->data[q->pos++] = m;
++ return APR_SUCCESS;
++}
++
++static APR_INLINE apr_skiplistnode *skiplist_qpop(apr_skiplist_q *q)
++{
++ return (q->pos > 0) ? q->data[--q->pos] : NULL;
++}
++
++static APR_INLINE void skiplist_qclear(apr_skiplist_q *q)
++{
++ q->pos = 0;
++}
++
++static apr_skiplistnode *skiplist_new_node(apr_skiplist *sl)
++{
++ apr_skiplistnode *m = skiplist_qpop(&sl->nodes_q);
++ if (!m) {
++ if (sl->pool) {
++ m = apr_palloc(sl->pool, sizeof *m);
++ }
++ else {
++ m = malloc(sizeof *m);
++ }
++ }
++ return m;
++}
++
++static apr_status_t skiplist_free_node(apr_skiplist *sl, apr_skiplistnode *m)
++{
++ return skiplist_qpush(&sl->nodes_q, m);
++}
++
++static apr_status_t skiplisti_init(apr_skiplist **s, apr_pool_t *p)
++{
++ apr_skiplist *sl;
++ if (p) {
++ sl = apr_pcalloc(p, sizeof(apr_skiplist));
++ sl->memlist = apr_array_make(p, 20, sizeof(memlist_t));
++ sl->pool = sl->nodes_q.p = sl->stack_q.p = p;
++ }
++ else {
++ sl = calloc(1, sizeof(apr_skiplist));
++ if (!sl) {
++ return APR_ENOMEM;
++ }
++ }
++ *s = sl;
++ return APR_SUCCESS;
++}
++
++static int indexing_comp(void *a, void *b)
++{
++ void *ac = (void *) (((apr_skiplist *) a)->compare);
++ void *bc = (void *) (((apr_skiplist *) b)->compare);
++ return ((ac < bc) ? -1 : ((ac > bc) ? 1 : 0));
++}
++
++static int indexing_compk(void *ac, void *b)
++{
++ void *bc = (void *) (((apr_skiplist *) b)->compare);
++ return ((ac < bc) ? -1 : ((ac > bc) ? 1 : 0));
++}
++
++APR_DECLARE(apr_status_t) apr_skiplist_init(apr_skiplist **s, apr_pool_t *p)
++{
++ apr_skiplist *sl;
++ skiplisti_init(s, p);
++ sl = *s;
++ skiplisti_init(&(sl->index), p);
++ apr_skiplist_set_compare(sl->index, indexing_comp, indexing_compk);
++ return APR_SUCCESS;
++}
++
++APR_DECLARE(void) apr_skiplist_set_compare(apr_skiplist *sl,
++ apr_skiplist_compare comp,
++ apr_skiplist_compare compk)
++{
++ if (sl->compare && sl->comparek) {
++ apr_skiplist_add_index(sl, comp, compk);
++ }
++ else {
++ sl->compare = comp;
++ sl->comparek = compk;
++ }
++}
++
++APR_DECLARE(void) apr_skiplist_add_index(apr_skiplist *sl,
++ apr_skiplist_compare comp,
++ apr_skiplist_compare compk)
++{
++ apr_skiplistnode *m;
++ apr_skiplist *ni;
++ int icount = 0;
++ apr_skiplist_find(sl->index, (void *)comp, &m);
++ if (m) {
++ return; /* Index already there! */
++ }
++ skiplisti_init(&ni, sl->pool);
++ apr_skiplist_set_compare(ni, comp, compk);
++ /* Build the new index... This can be expensive! */
++ m = apr_skiplist_insert(sl->index, ni);
++ while (m->prev) {
++ m = m->prev;
++ icount++;
++ }
++ for (m = apr_skiplist_getlist(sl); m; apr_skiplist_next(sl, &m)) {
++ int j = icount - 1;
++ apr_skiplistnode *nsln;
++ nsln = apr_skiplist_insert(ni, m->data);
++ /* skip from main index down list */
++ while (j > 0) {
++ m = m->nextindex;
++ j--;
++ }
++ /* insert this node in the indexlist after m */
++ nsln->nextindex = m->nextindex;
++ if (m->nextindex) {
++ m->nextindex->previndex = nsln;
++ }
++ nsln->previndex = m;
++ m->nextindex = nsln;
++ }
++}
++
++static int skiplisti_find_compare(apr_skiplist *sl, void *data,
++ apr_skiplistnode **ret,
++ apr_skiplist_compare comp)
++{
++ int count = 0;
++ apr_skiplistnode *m;
++ m = sl->top;
++ while (m) {
++ if (m->next) {
++ int compared = comp(data, m->next->data);
++ if (compared == 0) {
++ m = m->next;
++ while (m->down) {
++ m = m->down;
++ }
++ *ret = m;
++ return count;
++ }
++ if (compared > 0) {
++ m = m->next;
++ count++;
++ continue;
++ }
++ }
++ m = m->down;
++ count++;
++ }
++ *ret = NULL;
++ return count;
++}
++
++APR_DECLARE(void *) apr_skiplist_find_compare(apr_skiplist *sli, void *data,
++ apr_skiplistnode **iter,
++ apr_skiplist_compare comp)
++{
++ apr_skiplistnode *m;
++ apr_skiplist *sl;
++ if (!comp) {
++ if (iter) {
++ *iter = NULL;
++ }
++ return NULL;
++ }
++ if (comp == sli->compare || !sli->index) {
++ sl = sli;
++ }
++ else {
++ apr_skiplist_find(sli->index, (void *)comp, &m);
++ if (!m) {
++ if (iter) {
++ *iter = NULL;
++ }
++ return NULL;
++ }
++ sl = (apr_skiplist *) m->data;
++ }
++ skiplisti_find_compare(sl, data, &m, sl->comparek);
++ if (iter) {
++ *iter = m;
++ }
++ return (m) ? m->data : NULL;
++}
++
++APR_DECLARE(void *) apr_skiplist_find(apr_skiplist *sl, void *data, apr_skiplistnode **iter)
++{
++ return apr_skiplist_find_compare(sl, data, iter, sl->compare);
++}
++
++
++APR_DECLARE(apr_skiplistnode *) apr_skiplist_getlist(apr_skiplist *sl)
++{
++ if (!sl->bottom) {
++ return NULL;
++ }
++ return sl->bottom->next;
++}
++
++APR_DECLARE(void *) apr_skiplist_next(apr_skiplist *sl, apr_skiplistnode **iter)
++{
++ if (!*iter) {
++ return NULL;
++ }
++ *iter = (*iter)->next;
++ return (*iter) ? ((*iter)->data) : NULL;
++}
++
++APR_DECLARE(void *) apr_skiplist_previous(apr_skiplist *sl, apr_skiplistnode **iter)
++{
++ if (!*iter) {
++ return NULL;
++ }
++ *iter = (*iter)->prev;
++ return (*iter) ? ((*iter)->data) : NULL;
++}
++
++static APR_INLINE int skiplist_height(const apr_skiplist *sl)
++{
++ /* Skiplists (even empty) always have a top node, although this
++ * implementation defers its creation until the first insert, or
++ * deletes it with the last remove. We want the real height here.
++ */
++ return sl->height ? sl->height : 1;
++}
++
++APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert_compare(apr_skiplist *sl, void *data,
++ apr_skiplist_compare comp)
++{
++ apr_skiplistnode *m, *p, *tmp, *ret = NULL;
++ int ch, nh = 1;
++
++ if (!comp) {
++ return NULL;
++ }
++
++ ch = skiplist_height(sl);
++ if (sl->preheight) {
++ while (nh < sl->preheight && get_b_rand()) {
++ nh++;
++ }
++ }
++ else {
++ while (nh <= ch && get_b_rand()) {
++ nh++;
++ }
++ }
++
++ /* Now we have in nh the height at which we wish to insert our new node,
++ * and in ch the current height: don't create skip paths to the inserted
++ * element until the walk down through the tree (which decrements ch)
++ * reaches nh. From there, any walk down pushes the current node on a
++ * stack (the node(s) after which we would insert) to pop back through
++ * for insertion later.
++ */
++ m = sl->top;
++ while (m) {
++ if (m->next) {
++ int compared = comp(data, m->next->data);
++ if (compared == 0) {
++ /* Keep the existing element(s) */
++ skiplist_qclear(&sl->stack_q);
++ return NULL;
++ }
++ if (compared > 0) {
++ m = m->next;
++ continue;
++ }
++ }
++ if (ch <= nh) {
++ /* push on stack */
++ skiplist_qpush(&sl->stack_q, m);
++ }
++ m = m->down;
++ ch--;
++ }
++ /* Pop the stack and insert nodes */
++ p = NULL;
++ while ((m = skiplist_qpop(&sl->stack_q))) {
++ tmp = skiplist_new_node(sl);
++ tmp->next = m->next;
++ if (m->next) {
++ m->next->prev = tmp;
++ }
++ m->next = tmp;
++ tmp->prev = m;
++ tmp->up = NULL;
++ tmp->nextindex = tmp->previndex = NULL;
++ tmp->down = p;
++ if (p) {
++ p->up = tmp;
++ }
++ else {
++ /* This sets ret to the bottom-most node we are inserting */
++ ret = tmp;
++ }
++ tmp->data = data;
++ tmp->sl = sl;
++ p = tmp;
++ }
++
++ /* Now we are sure the node is inserted, grow our tree to 'nh' tall */
++ for (; sl->height < nh; sl->height++) {
++ m = skiplist_new_node(sl);
++ tmp = skiplist_new_node(sl);
++ m->up = m->prev = m->nextindex = m->previndex = NULL;
++ m->next = tmp;
++ m->down = sl->top;
++ m->data = NULL;
++ m->sl = sl;
++ if (sl->top) {
++ sl->top->up = m;
++ }
++ else {
++ sl->bottom = sl->bottomend = m;
++ }
++ sl->top = sl->topend = tmp->prev = m;
++ tmp->up = tmp->next = tmp->nextindex = tmp->previndex = NULL;
++ tmp->down = p;
++ tmp->data = data;
++ tmp->sl = sl;
++ if (p) {
++ p->up = tmp;
++ }
++ else {
++ /* This sets ret to the bottom-most node we are inserting */
++ ret = tmp;
++ }
++ p = tmp;
++ }
++ if (sl->index != NULL) {
++ /*
++ * this is a external insertion, we must insert into each index as
++ * well
++ */
++ apr_skiplistnode *ni, *li;
++ li = ret;
++ for (p = apr_skiplist_getlist(sl->index); p; apr_skiplist_next(sl->index, &p)) {
++ apr_skiplist *sli = (apr_skiplist *)p->data;
++ ni = apr_skiplist_insert_compare(sli, ret->data, sli->compare);
++ li->nextindex = ni;
++ ni->previndex = li;
++ li = ni;
++ }
++ }
++ sl->size++;
++ return ret;
++}
++
++APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert(apr_skiplist *sl, void *data)
++{
++ return apr_skiplist_insert_compare(sl, data, sl->compare);
++}
++
++#if 0
++void skiplist_print_struct(apr_skiplist * sl, char *prefix)
++{
++ apr_skiplistnode *p, *q;
++ fprintf(stderr, "Skiplist Structure (height: %d)\n", sl->height);
++ p = sl->bottom;
++ while (p) {
++ q = p;
++ fprintf(stderr, prefix);
++ while (q) {
++ fprintf(stderr, "%p ", q->data);
++ q = q->up;
++ }
++ fprintf(stderr, "\n");
++ p = p->next;
++ }
++}
++#endif
++
++static int skiplisti_remove(apr_skiplist *sl, apr_skiplistnode *m, apr_skiplist_freefunc myfree)
++{
++ apr_skiplistnode *p;
++ if (!m) {
++ return 0;
++ }
++ if (m->nextindex) {
++ skiplisti_remove(m->nextindex->sl, m->nextindex, NULL);
++ }
++ while (m->up) {
++ m = m->up;
++ }
++ while (m) {
++ p = m;
++ p->prev->next = p->next;/* take me out of the list */
++ if (p->next) {
++ p->next->prev = p->prev; /* take me out of the list */
++ }
++ m = m->down;
++ /* This only frees the actual data in the bottom one */
++ if (!m && myfree && p->data) {
++ myfree(p->data);
++ }
++ skiplist_free_node(sl, p);
++ }
++ sl->size--;
++ while (sl->top && sl->top->next == NULL) {
++ /* While the row is empty and we are not on the bottom row */
++ p = sl->top;
++ sl->top = sl->top->down;/* Move top down one */
++ if (sl->top) {
++ sl->top->up = NULL; /* Make it think its the top */
++ }
++ skiplist_free_node(sl, p);
++ sl->height--;
++ }
++ if (!sl->top) {
++ sl->bottom = sl->bottomend = NULL;
++ sl->topend = NULL;
++ }
++ return skiplist_height(sl);
++}
++
++APR_DECLARE(int) apr_skiplist_remove_compare(apr_skiplist *sli,
++ void *data,
++ apr_skiplist_freefunc myfree, apr_skiplist_compare comp)
++{
++ apr_skiplistnode *m;
++ apr_skiplist *sl;
++ if (!comp) {
++ return 0;
++ }
++ if (comp == sli->comparek || !sli->index) {
++ sl = sli;
++ }
++ else {
++ apr_skiplist_find(sli->index, (void *)comp, &m);
++ if (!m) {
++ return 0;
++ }
++ sl = (apr_skiplist *) m->data;
++ }
++ skiplisti_find_compare(sl, data, &m, comp);
++ if (!m) {
++ return 0;
++ }
++ while (m->previndex) {
++ m = m->previndex;
++ }
++ return skiplisti_remove(sl, m, myfree);
++}
++
++APR_DECLARE(int) apr_skiplist_remove(apr_skiplist *sl, void *data, apr_skiplist_freefunc myfree)
++{
++ return apr_skiplist_remove_compare(sl, data, myfree, sl->comparek);
++}
++
++APR_DECLARE(void) apr_skiplist_remove_all(apr_skiplist *sl, apr_skiplist_freefunc myfree)
++{
++ /*
++ * This must remove even the place holder nodes (bottom though top)
++ * because we specify in the API that one can free the Skiplist after
++ * making this call without memory leaks
++ */
++ apr_skiplistnode *m, *p, *u;
++ m = sl->bottom;
++ while (m) {
++ p = m->next;
++ if (myfree && p && p->data) {
++ myfree(p->data);
++ }
++ do {
++ u = m->up;
++ skiplist_free_node(sl, m);
++ m = u;
++ } while (m);
++ m = p;
++ }
++ sl->top = sl->bottom = NULL;
++ sl->topend = sl->bottomend = NULL;
++ sl->height = 0;
++ sl->size = 0;
++}
++
++APR_DECLARE(void *) apr_skiplist_pop(apr_skiplist *a, apr_skiplist_freefunc myfree)
++{
++ apr_skiplistnode *sln;
++ void *data = NULL;
++ sln = apr_skiplist_getlist(a);
++ if (sln) {
++ data = sln->data;
++ skiplisti_remove(a, sln, myfree);
++ }
++ return data;
++}
++
++APR_DECLARE(void *) apr_skiplist_peek(apr_skiplist *a)
++{
++ apr_skiplistnode *sln;
++ sln = apr_skiplist_getlist(a);
++ if (sln) {
++ return sln->data;
++ }
++ return NULL;
++}
++
++static void skiplisti_destroy(void *vsl)
++{
++ apr_skiplist_destroy(vsl, NULL);
++}
++
++APR_DECLARE(void) apr_skiplist_destroy(apr_skiplist *sl, apr_skiplist_freefunc myfree)
++{
++ while (apr_skiplist_pop(sl->index, skiplisti_destroy) != NULL)
++ ;
++ apr_skiplist_remove_all(sl, myfree);
++ if (!sl->pool) {
++ while (sl->nodes_q.pos)
++ free(sl->nodes_q.data[--sl->nodes_q.pos]);
++ free(sl->nodes_q.data);
++ free(sl->stack_q.data);
++ free(sl);
++ }
++}
++
++APR_DECLARE(apr_skiplist *) apr_skiplist_merge(apr_skiplist *sl1, apr_skiplist *sl2)
++{
++ /* Check integrity! */
++ apr_skiplist temp;
++ struct apr_skiplistnode *b2;
++ if (sl1->bottomend == NULL || sl1->bottomend->prev == NULL) {
++ apr_skiplist_remove_all(sl1, NULL);
++ temp = *sl1;
++ *sl1 = *sl2;
++ *sl2 = temp;
++ /* swap them so that sl2 can be freed normally upon return. */
++ return sl1;
++ }
++ if(sl2->bottom == NULL || sl2->bottom->next == NULL) {
++ apr_skiplist_remove_all(sl2, NULL);
++ return sl1;
++ }
++ /* This is what makes it brute force... Just insert :/ */
++ b2 = apr_skiplist_getlist(sl2);
++ while (b2) {
++ apr_skiplist_insert(sl1, b2->data);
++ apr_skiplist_next(sl2, &b2);
++ }
++ apr_skiplist_remove_all(sl2, NULL);
++ return sl1;
++}
+diff --git a/server/mpm/event/apr_skiplist.h b/server/mpm/event/apr_skiplist.h
+new file mode 100644
+index 0000000..f56ff22
+--- /dev/null
++++ b/server/mpm/event/apr_skiplist.h
+@@ -0,0 +1,263 @@
++/* Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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 APR_SKIPLIST_H
++#define APR_SKIPLIST_H
++/**
++ * @file apr_skiplist.h
++ * @brief APR skip list implementation
++ */
++
++#include "apr.h"
++#include "apr_portable.h"
++#include --enable-suexec
option to let
- APACI accept your request for using the suEXEC feature.
-
-+ --enable-suexec-capabilities
suexec
binary is installed "setuid/setgid
-+ root", which allows it to run with the full privileges of the
-+ root user. If this option is used, the suexec
-+ binary will instead be installed with only the setuid/setgid
-+ "capability" bits set, which is the subset of full root
-+ priviliges required for suexec operation. Note that
-+ the suexec
binary may not be able to write to a log
-+ file in this mode; it is recommended that the
-+ --with-suexec-syslog --without-suexec-logfile
-+ options are used in conjunction with this mode, so that syslog
-+ logging is used instead.--with-suexec-bin=PATH
suexec
binary must be hard-coded
-@@ -433,6 +448,12 @@
- "suexec_log
" and located in your standard logfile
- directory (--logfiledir
).--with-suexec-syslog
--without-suexec-logfile
.--with-suexec-safepath=PATH
--with-suexec-logfile
-- option as indicated above. If you feel you have configured and
-- installed the wrapper properly, have a look at this log and the
-- error_log for the server to see where you may have gone astray.--with-suexec-syslog
-+ is used. If you feel you have configured and
-+ installed the wrapper properly, have a look at the log and the
-+ error_log for the server to see where you may have gone astray.
-+ The output of "suexec -V"
will show the options
-+ used to compile suexec, if using a binary distribution.