diff --git a/00-base.conf b/00-base.conf
new file mode 100644
index 0000000..28dacb3
--- /dev/null
+++ b/00-base.conf
@@ -0,0 +1,68 @@
+#
+# This file loads most of the modules included with the Apache HTTP
+# Server itself.
+#
+
+LoadModule access_compat_module modules/mod_access_compat.so
+LoadModule actions_module modules/mod_actions.so
+LoadModule alias_module modules/mod_alias.so
+LoadModule allowmethods_module modules/mod_allowmethods.so
+LoadModule auth_basic_module modules/mod_auth_basic.so
+LoadModule auth_digest_module modules/mod_auth_digest.so
+LoadModule authn_anon_module modules/mod_authn_anon.so
+LoadModule authn_core_module modules/mod_authn_core.so
+LoadModule authn_dbd_module modules/mod_authn_dbd.so
+LoadModule authn_dbm_module modules/mod_authn_dbm.so
+LoadModule authn_file_module modules/mod_authn_file.so
+LoadModule authn_socache_module modules/mod_authn_socache.so
+LoadModule authz_core_module modules/mod_authz_core.so
+LoadModule authz_dbd_module modules/mod_authz_dbd.so
+LoadModule authz_dbm_module modules/mod_authz_dbm.so
+LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
+LoadModule authz_host_module modules/mod_authz_host.so
+LoadModule authz_owner_module modules/mod_authz_owner.so
+LoadModule authz_user_module modules/mod_authz_user.so
+LoadModule autoindex_module modules/mod_autoindex.so
+LoadModule brotli_module modules/mod_brotli.so
+LoadModule cache_module modules/mod_cache.so
+LoadModule cache_disk_module modules/mod_cache_disk.so
+LoadModule cache_socache_module modules/mod_cache_socache.so
+LoadModule data_module modules/mod_data.so
+LoadModule dbd_module modules/mod_dbd.so
+LoadModule deflate_module modules/mod_deflate.so
+LoadModule dir_module modules/mod_dir.so
+LoadModule dumpio_module modules/mod_dumpio.so
+LoadModule echo_module modules/mod_echo.so
+LoadModule env_module modules/mod_env.so
+LoadModule expires_module modules/mod_expires.so
+LoadModule ext_filter_module modules/mod_ext_filter.so
+LoadModule filter_module modules/mod_filter.so
+LoadModule headers_module modules/mod_headers.so
+LoadModule include_module modules/mod_include.so
+LoadModule info_module modules/mod_info.so
+LoadModule log_config_module modules/mod_log_config.so
+LoadModule logio_module modules/mod_logio.so
+LoadModule macro_module modules/mod_macro.so
+LoadModule mime_magic_module modules/mod_mime_magic.so
+LoadModule mime_module modules/mod_mime.so
+LoadModule negotiation_module modules/mod_negotiation.so
+LoadModule remoteip_module modules/mod_remoteip.so
+LoadModule reqtimeout_module modules/mod_reqtimeout.so
+LoadModule request_module modules/mod_request.so
+LoadModule rewrite_module modules/mod_rewrite.so
+LoadModule setenvif_module modules/mod_setenvif.so
+LoadModule slotmem_plain_module modules/mod_slotmem_plain.so
+LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
+LoadModule socache_dbm_module modules/mod_socache_dbm.so
+LoadModule socache_memcache_module modules/mod_socache_memcache.so
+LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
+LoadModule status_module modules/mod_status.so
+LoadModule substitute_module modules/mod_substitute.so
+LoadModule suexec_module modules/mod_suexec.so
+LoadModule unique_id_module modules/mod_unique_id.so
+LoadModule unixd_module modules/mod_unixd.so
+LoadModule userdir_module modules/mod_userdir.so
+LoadModule version_module modules/mod_version.so
+LoadModule vhost_alias_module modules/mod_vhost_alias.so
+LoadModule watchdog_module modules/mod_watchdog.so
+
diff --git a/00-dav.conf b/00-dav.conf
new file mode 100644
index 0000000..e6af8de
--- /dev/null
+++ b/00-dav.conf
@@ -0,0 +1,3 @@
+LoadModule dav_module modules/mod_dav.so
+LoadModule dav_fs_module modules/mod_dav_fs.so
+LoadModule dav_lock_module modules/mod_dav_lock.so
diff --git a/00-lua.conf b/00-lua.conf
new file mode 100644
index 0000000..9e0d0db
--- /dev/null
+++ b/00-lua.conf
@@ -0,0 +1 @@
+LoadModule lua_module modules/mod_lua.so
diff --git a/00-mpm.conf b/00-mpm.conf
new file mode 100644
index 0000000..b15f913
--- /dev/null
+++ b/00-mpm.conf
@@ -0,0 +1,23 @@
+# Select the MPM module which should be used by uncommenting exactly
+# one of the following LoadModule lines. See the httpd.service(8) man
+# page for more information on changing the MPM.
+
+# prefork MPM: Implements a non-threaded, pre-forking web server
+# See: http://httpd.apache.org/docs/2.4/mod/prefork.html
+#
+# NOTE: If enabling prefork, the httpd_graceful_shutdown SELinux
+# boolean should be enabled, to allow graceful stop/shutdown.
+#
+#LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
+
+# worker MPM: Multi-Processing Module implementing a hybrid
+# multi-threaded multi-process web server
+# See: http://httpd.apache.org/docs/2.4/mod/worker.html
+#
+#LoadModule mpm_worker_module modules/mod_mpm_worker.so
+
+# event MPM: A variant of the worker MPM with the goal of consuming
+# threads only for connections with active processing
+# See: http://httpd.apache.org/docs/2.4/mod/event.html
+#
+#LoadModule mpm_event_module modules/mod_mpm_event.so
diff --git a/00-optional.conf b/00-optional.conf
new file mode 100644
index 0000000..ef584ec
--- /dev/null
+++ b/00-optional.conf
@@ -0,0 +1,18 @@
+#
+# This file lists modules included with the Apache HTTP Server
+# which are not enabled by default.
+#
+
+#LoadModule asis_module modules/mod_asis.so
+#LoadModule buffer_module modules/mod_buffer.so
+#LoadModule heartbeat_module modules/mod_heartbeat.so
+#LoadModule heartmonitor_module modules/mod_heartmonitor.so
+#LoadModule usertrack_module modules/mod_usertrack.so
+#LoadModule dialup_module modules/mod_dialup.so
+#LoadModule charset_lite_module modules/mod_charset_lite.so
+#LoadModule log_debug_module modules/mod_log_debug.so
+#LoadModule log_forensic_module modules/mod_log_forensic.so
+#LoadModule ratelimit_module modules/mod_ratelimit.so
+#LoadModule reflector_module modules/mod_reflector.so
+#LoadModule sed_module modules/mod_sed.so
+#LoadModule speling_module modules/mod_speling.so
diff --git a/00-proxy.conf b/00-proxy.conf
new file mode 100644
index 0000000..f0f84c2
--- /dev/null
+++ b/00-proxy.conf
@@ -0,0 +1,18 @@
+# This file configures all the proxy modules:
+LoadModule proxy_module modules/mod_proxy.so
+LoadModule lbmethod_bybusyness_module modules/mod_lbmethod_bybusyness.so
+LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
+LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so
+LoadModule lbmethod_heartbeat_module modules/mod_lbmethod_heartbeat.so
+LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
+LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
+LoadModule proxy_connect_module modules/mod_proxy_connect.so
+LoadModule proxy_express_module modules/mod_proxy_express.so
+LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
+LoadModule proxy_fdpass_module modules/mod_proxy_fdpass.so
+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/00-proxyhtml.conf b/00-proxyhtml.conf
new file mode 100644
index 0000000..9a9b107
--- /dev/null
+++ b/00-proxyhtml.conf
@@ -0,0 +1,3 @@
+# This file configures mod_proxy_html and mod_xml2enc:
+LoadModule xml2enc_module modules/mod_xml2enc.so
+LoadModule proxy_html_module modules/mod_proxy_html.so
diff --git a/00-ssl.conf b/00-ssl.conf
new file mode 100644
index 0000000..53235cd
--- /dev/null
+++ b/00-ssl.conf
@@ -0,0 +1 @@
+LoadModule ssl_module modules/mod_ssl.so
diff --git a/00-systemd.conf b/00-systemd.conf
new file mode 100644
index 0000000..b208c97
--- /dev/null
+++ b/00-systemd.conf
@@ -0,0 +1,2 @@
+# This file configures systemd module:
+LoadModule systemd_module modules/mod_systemd.so
diff --git a/01-cgi.conf b/01-cgi.conf
new file mode 100644
index 0000000..5b8b936
--- /dev/null
+++ b/01-cgi.conf
@@ -0,0 +1,14 @@
+# This configuration file loads a CGI module appropriate to the MPM
+# which has been configured in 00-mpm.conf. mod_cgid should be used
+# with a threaded MPM; mod_cgi with the prefork MPM.
+
+
+ LoadModule cgid_module modules/mod_cgid.so
+
+
+ LoadModule cgid_module modules/mod_cgid.so
+
+
+ LoadModule cgi_module modules/mod_cgi.so
+
+
diff --git a/01-ldap.conf b/01-ldap.conf
new file mode 100644
index 0000000..f2ac2a2
--- /dev/null
+++ b/01-ldap.conf
@@ -0,0 +1,3 @@
+# This file configures the LDAP modules:
+LoadModule ldap_module modules/mod_ldap.so
+LoadModule authnz_ldap_module modules/mod_authnz_ldap.so
diff --git a/01-md.conf b/01-md.conf
new file mode 100644
index 0000000..2739202
--- /dev/null
+++ b/01-md.conf
@@ -0,0 +1 @@
+LoadModule md_module modules/mod_md.so
diff --git a/01-session.conf b/01-session.conf
new file mode 100644
index 0000000..f8d4d92
--- /dev/null
+++ b/01-session.conf
@@ -0,0 +1,6 @@
+LoadModule session_module modules/mod_session.so
+LoadModule session_cookie_module modules/mod_session_cookie.so
+LoadModule session_dbd_module modules/mod_session_dbd.so
+LoadModule auth_form_module modules/mod_auth_form.so
+
+#LoadModule session_crypto_module modules/mod_session_crypto.so
diff --git a/10-listen443.conf b/10-listen443.conf
new file mode 100644
index 0000000..7e2df97
--- /dev/null
+++ b/10-listen443.conf
@@ -0,0 +1,5 @@
+# This file is part of mod_ssl. It enables listening on port 443 when
+# socket activation is used.
+
+[Socket]
+ListenStream=443
diff --git a/CVE-2018-11763.patch b/CVE-2018-11763.patch
new file mode 100644
index 0000000..fc2de14
--- /dev/null
+++ b/CVE-2018-11763.patch
@@ -0,0 +1,460 @@
+diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c
+index 814d2a96..a1b31d2b 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 486938b0..df2a8624 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,7 +119,9 @@ 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/CVE-2018-17189.patch b/CVE-2018-17189.patch
new file mode 100644
index 0000000..02aaf24
--- /dev/null
+++ b/CVE-2018-17189.patch
@@ -0,0 +1,20 @@
+diff --git a/modules/http2/h2_conn.c b/modules/http2/h2_conn.c
+index 2e95659..f7f81be 100644
+--- a/modules/http2/h2_conn.c
++++ b/modules/http2/h2_conn.c
+@@ -354,6 +354,15 @@ apr_status_t h2_slave_run_pre_connection(conn_rec *slave, apr_socket_t *csd)
+ * (Not necessarily in pre_connection, but later. Set it here, so it
+ * is in place.) */
+ slave->keepalives = 1;
++ /* We signal that this connection will be closed after the request.
++ * Which is true in that sense that we throw away all traffic data
++ * on this slave connection after each requests. Although we might
++ * reuse internal structures like memory pools.
++ * The wanted effect of this is that httpd does not try to clean up
++ * any dangling data on this connection when a request is done. Which
++ * is unneccessary on a h2 stream.
++ */
++ slave->keepalive = AP_CONN_CLOSE;
+ return ap_run_pre_connection(slave, csd);
+ }
+ return APR_SUCCESS;
diff --git a/CVE-2019-0211.patch b/CVE-2019-0211.patch
new file mode 100644
index 0000000..e117f52
--- /dev/null
+++ b/CVE-2019-0211.patch
@@ -0,0 +1,220 @@
+MPMs unix: bind the bucket number of each child to its slot number
+
+We need not remember each child's bucket number in SHM for restarts, for the
+lifetime of the httpd main process the bucket number can be bound to the slot
+number such that: bucket = slot % num_buckets.
+
+This both simplifies the logic and helps children maintenance per bucket in
+threaded MPMs, where previously perform_idle_server_maintenance() could create
+or kill children processes for the buckets it was not in charge of.
+
+diff --git a/include/scoreboard.h b/include/scoreboard.h
+index 57cf3df..b714a8c 100644
+--- a/include/scoreboard.h
++++ b/include/scoreboard.h
+@@ -143,7 +143,9 @@ struct process_score {
+ apr_uint32_t lingering_close; /* async connections in lingering close */
+ apr_uint32_t keep_alive; /* async connections in keep alive */
+ apr_uint32_t suspended; /* connections suspended by some module */
+- int bucket; /* Listener bucket used by this child */
++ int bucket; /* Listener bucket used by this child; this field is DEPRECATED
++ * and no longer updated by the MPMs (i.e. always zero).
++ */
+ };
+
+ /* Scoreboard is now in 'local' memory, since it isn't updated once created,
+diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c
+index ffe8a23..048ae61 100644
+--- a/server/mpm/event/event.c
++++ b/server/mpm/event/event.c
+@@ -2695,7 +2695,6 @@ static int make_child(server_rec * s, int slot, int bucket)
+
+ ap_scoreboard_image->parent[slot].quiescing = 0;
+ ap_scoreboard_image->parent[slot].not_accepting = 0;
+- ap_scoreboard_image->parent[slot].bucket = bucket;
+ event_note_child_started(slot, pid);
+ active_daemons++;
+ retained->total_daemons++;
+@@ -2734,6 +2733,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
+ * that threads_per_child is always > 0 */
+ int status = SERVER_DEAD;
+ int child_threads_active = 0;
++ int bucket = i % num_buckets;
+
+ if (i >= retained->max_daemons_limit &&
+ free_length == retained->idle_spawn_rate[child_bucket]) {
+@@ -2757,7 +2757,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
+ */
+ if (status <= SERVER_READY && !ps->quiescing && !ps->not_accepting
+ && ps->generation == retained->mpm->my_generation
+- && ps->bucket == child_bucket)
++ && bucket == child_bucket)
+ {
+ ++idle_thread_count;
+ }
+@@ -2768,7 +2768,9 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
+ last_non_dead = i;
+ }
+ active_thread_count += child_threads_active;
+- if (!ps->pid && free_length < retained->idle_spawn_rate[child_bucket])
++ if (!ps->pid
++ && bucket == child_bucket
++ && free_length < retained->idle_spawn_rate[child_bucket])
+ free_slots[free_length++] = i;
+ else if (child_threads_active == threads_per_child)
+ had_healthy_child = 1;
+@@ -2951,13 +2953,14 @@ static void server_main_loop(int remaining_children_to_start, int num_buckets)
+ retained->total_daemons--;
+ if (processed_status == APEXIT_CHILDSICK) {
+ /* resource shortage, minimize the fork rate */
+- retained->idle_spawn_rate[ps->bucket] = 1;
++ retained->idle_spawn_rate[child_slot % num_buckets] = 1;
+ }
+ else if (remaining_children_to_start) {
+ /* we're still doing a 1-for-1 replacement of dead
+ * children with new children
+ */
+- make_child(ap_server_conf, child_slot, ps->bucket);
++ make_child(ap_server_conf, child_slot,
++ child_slot % num_buckets);
+ --remaining_children_to_start;
+ }
+ }
+diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c
+index 8efda72..7c00625 100644
+--- a/server/mpm/prefork/prefork.c
++++ b/server/mpm/prefork/prefork.c
+@@ -637,8 +637,9 @@ static void child_main(int child_num_arg, int child_bucket)
+ }
+
+
+-static int make_child(server_rec *s, int slot, int bucket)
++static int make_child(server_rec *s, int slot)
+ {
++ int bucket = slot % retained->mpm->num_buckets;
+ int pid;
+
+ if (slot + 1 > retained->max_daemons_limit) {
+@@ -716,7 +717,6 @@ static int make_child(server_rec *s, int slot, int bucket)
+ child_main(slot, bucket);
+ }
+
+- ap_scoreboard_image->parent[slot].bucket = bucket;
+ prefork_note_child_started(slot, pid);
+
+ return 0;
+@@ -732,7 +732,7 @@ static void startup_children(int number_to_start)
+ if (ap_scoreboard_image->servers[i][0].status != SERVER_DEAD) {
+ continue;
+ }
+- if (make_child(ap_server_conf, i, i % retained->mpm->num_buckets) < 0) {
++ if (make_child(ap_server_conf, i) < 0) {
+ break;
+ }
+ --number_to_start;
+@@ -741,8 +741,6 @@ static void startup_children(int number_to_start)
+
+ static void perform_idle_server_maintenance(apr_pool_t *p)
+ {
+- static int bucket_make_child_record = -1;
+- static int bucket_kill_child_record = -1;
+ int i;
+ int idle_count;
+ worker_score *ws;
+@@ -789,6 +787,7 @@ static void perform_idle_server_maintenance(apr_pool_t *p)
+ }
+ retained->max_daemons_limit = last_non_dead + 1;
+ if (idle_count > ap_daemons_max_free) {
++ static int bucket_kill_child_record = -1;
+ /* kill off one child... we use the pod because that'll cause it to
+ * shut down gracefully, in case it happened to pick up a request
+ * while we were counting
+@@ -819,10 +818,7 @@ static void perform_idle_server_maintenance(apr_pool_t *p)
+ idle_count, total_non_dead);
+ }
+ for (i = 0; i < free_length; ++i) {
+- bucket_make_child_record++;
+- bucket_make_child_record %= retained->mpm->num_buckets;
+- make_child(ap_server_conf, free_slots[i],
+- bucket_make_child_record);
++ make_child(ap_server_conf, free_slots[i]);
+ }
+ /* the next time around we want to spawn twice as many if this
+ * wasn't good enough, but not if we've just done a graceful
+@@ -867,7 +863,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
+
+ if (one_process) {
+ AP_MONCONTROL(1);
+- make_child(ap_server_conf, 0, 0);
++ make_child(ap_server_conf, 0);
+ /* NOTREACHED */
+ ap_assert(0);
+ return !OK;
+@@ -976,8 +972,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
+ /* we're still doing a 1-for-1 replacement of dead
+ * children with new children
+ */
+- make_child(ap_server_conf, child_slot,
+- ap_get_scoreboard_process(child_slot)->bucket);
++ make_child(ap_server_conf, child_slot);
+ --remaining_children_to_start;
+ }
+ #if APR_HAS_OTHER_CHILD
+diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c
+index 8012fe2..a927942 100644
+--- a/server/mpm/worker/worker.c
++++ b/server/mpm/worker/worker.c
+@@ -1339,7 +1339,6 @@ static int make_child(server_rec *s, int slot, int bucket)
+ worker_note_child_lost_slot(slot, pid);
+ }
+ ap_scoreboard_image->parent[slot].quiescing = 0;
+- ap_scoreboard_image->parent[slot].bucket = bucket;
+ worker_note_child_started(slot, pid);
+ return 0;
+ }
+@@ -1388,6 +1387,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
+ int any_dead_threads = 0;
+ int all_dead_threads = 1;
+ int child_threads_active = 0;
++ int bucket = i % num_buckets;
+
+ if (i >= retained->max_daemons_limit &&
+ totally_free_length == retained->idle_spawn_rate[child_bucket]) {
+@@ -1420,7 +1420,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
+ if (status <= SERVER_READY &&
+ !ps->quiescing &&
+ ps->generation == retained->mpm->my_generation &&
+- ps->bucket == child_bucket) {
++ bucket == child_bucket) {
+ ++idle_thread_count;
+ }
+ if (status >= SERVER_READY && status < SERVER_GRACEFUL) {
+@@ -1430,6 +1430,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
+ }
+ active_thread_count += child_threads_active;
+ if (any_dead_threads
++ && bucket == child_bucket
+ && totally_free_length < retained->idle_spawn_rate[child_bucket]
+ && free_length < MAX_SPAWN_RATE / num_buckets
+ && (!ps->pid /* no process in the slot */
+@@ -1615,14 +1616,15 @@ static void server_main_loop(int remaining_children_to_start, int num_buckets)
+ ps->quiescing = 0;
+ if (processed_status == APEXIT_CHILDSICK) {
+ /* resource shortage, minimize the fork rate */
+- retained->idle_spawn_rate[ps->bucket] = 1;
++ retained->idle_spawn_rate[child_slot % num_buckets] = 1;
+ }
+ else if (remaining_children_to_start
+ && child_slot < ap_daemons_limit) {
+ /* we're still doing a 1-for-1 replacement of dead
+ * children with new children
+ */
+- make_child(ap_server_conf, child_slot, ps->bucket);
++ make_child(ap_server_conf, child_slot,
++ child_slot % num_buckets);
+ --remaining_children_to_start;
+ }
+ }
+--
+2.19.1
+
diff --git a/CVE-2019-0215.patch b/CVE-2019-0215.patch
new file mode 100644
index 0000000..88ff2ba
--- /dev/null
+++ b/CVE-2019-0215.patch
@@ -0,0 +1,20 @@
+diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
+index cf841b0..7459503 100644
+--- a/modules/ssl/ssl_engine_kernel.c
++++ b/modules/ssl/ssl_engine_kernel.c
+@@ -1154,6 +1154,7 @@ static int ssl_hook_Access_modern(request_rec *r, SSLSrvConfigRec *sc, SSLDirCon
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, r->server);
+ apr_table_setn(r->notes, "error-notes",
+ "Reason: Cannot perform Post-Handshake Authentication.
");
++ SSL_set_verify(ssl, vmode_inplace, NULL);
+ return HTTP_FORBIDDEN;
+ }
+
+@@ -1175,6 +1176,7 @@ static int ssl_hook_Access_modern(request_rec *r, SSLSrvConfigRec *sc, SSLDirCon
+ * Finally check for acceptable renegotiation results
+ */
+ if (OK != (rc = ssl_check_post_client_verify(r, sc, dc, sslconn, ssl))) {
++ SSL_set_verify(ssl, vmode_inplace, NULL);
+ return rc;
+ }
+ }
diff --git a/CVE-2019-0220-1.patch b/CVE-2019-0220-1.patch
new file mode 100644
index 0000000..faf9aad
--- /dev/null
+++ b/CVE-2019-0220-1.patch
@@ -0,0 +1,203 @@
+From 9bc1917a27a2323e535aadb081e38172ae0e3fc2 Mon Sep 17 00:00:00 2001
+From: Stefan Eissing
+Date: Mon, 18 Mar 2019 08:49:59 +0000
+Subject: [PATCH] Merge of r1855705 from trunk:
+
+core: merge consecutive slashes in the path
+
+
+
+git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1855737 13f79535-47bb-0310-9956-ffa450edef68
+---
+ include/http_core.h | 2 +-
+ include/httpd.h | 14 ++++++++++++--
+ server/core.c | 13 +++++++++++++
+ server/request.c | 25 +++++++++----------------
+ server/util.c | 10 +++++++---
+ 5 files changed, 43 insertions(+), 21 deletions(-)
+
+diff --git a/include/http_core.h b/include/http_core.h
+index 35df5dc9601..8e109882244 100644
+--- a/include/http_core.h
++++ b/include/http_core.h
+@@ -740,7 +740,7 @@ typedef struct {
+ #define AP_HTTP_METHODS_LENIENT 1
+ #define AP_HTTP_METHODS_REGISTERED 2
+ char http_methods;
+-
++ unsigned int merge_slashes;
+ } core_server_config;
+
+ /* for AddOutputFiltersByType in core.c */
+diff --git a/include/httpd.h b/include/httpd.h
+index 65392f83546..99f7f041aea 100644
+--- a/include/httpd.h
++++ b/include/httpd.h
+@@ -1697,11 +1697,21 @@ AP_DECLARE(int) ap_unescape_url_keep2f(char *url, int decode_slashes);
+ AP_DECLARE(int) ap_unescape_urlencoded(char *query);
+
+ /**
+- * Convert all double slashes to single slashes
+- * @param name The string to convert
++ * Convert all double slashes to single slashes, except where significant
++ * to the filesystem on the current platform.
++ * @param name The string to convert, assumed to be a filesystem path
+ */
+ AP_DECLARE(void) ap_no2slash(char *name);
+
++/**
++ * Convert all double slashes to single slashes, except where significant
++ * to the filesystem on the current platform.
++ * @param name The string to convert
++ * @param is_fs_path if set to 0, the significance of any double-slashes is
++ * ignored.
++ */
++AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path);
++
+ /**
+ * Remove all ./ and xx/../ substrings from a file name. Also remove
+ * any leading ../ or /../ substrings.
+diff --git a/server/core.c b/server/core.c
+index e2a91c7a0c6..eacb54fecec 100644
+--- a/server/core.c
++++ b/server/core.c
+@@ -490,6 +490,7 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s)
+
+ conf->protocols = apr_array_make(a, 5, sizeof(const char *));
+ conf->protocols_honor_order = -1;
++ conf->merge_slashes = AP_CORE_CONFIG_UNSET;
+
+ return (void *)conf;
+ }
+@@ -555,6 +556,7 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
+ conf->protocols_honor_order = ((virt->protocols_honor_order < 0)?
+ base->protocols_honor_order :
+ virt->protocols_honor_order);
++ AP_CORE_MERGE_FLAG(merge_slashes, conf, base, virt);
+
+ return conf;
+ }
+@@ -1863,6 +1865,13 @@ static const char *set_qualify_redirect_url(cmd_parms *cmd, void *d_, int flag)
+ return NULL;
+ }
+
++static const char *set_core_server_flag(cmd_parms *cmd, void *s_, int flag)
++{
++ core_server_config *conf =
++ ap_get_core_module_config(cmd->server->module_config);
++ return ap_set_flag_slot(cmd, conf, flag);
++}
++
+ static const char *set_override_list(cmd_parms *cmd, void *d_, int argc, char *const argv[])
+ {
+ core_dir_config *d = d_;
+@@ -4562,6 +4571,10 @@ AP_INIT_ITERATE("HttpProtocolOptions", set_http_protocol_options, NULL, RSRC_CON
+ "'Unsafe' or 'Strict' (default). Sets HTTP acceptance rules"),
+ AP_INIT_ITERATE("RegisterHttpMethod", set_http_method, NULL, RSRC_CONF,
+ "Registers non-standard HTTP methods"),
++AP_INIT_FLAG("MergeSlashes", set_core_server_flag,
++ (void *)APR_OFFSETOF(core_server_config, merge_slashes),
++ RSRC_CONF,
++ "Controls whether consecutive slashes in the URI path are merged"),
+ { NULL }
+ };
+
+diff --git a/server/request.c b/server/request.c
+index dbe3e07f150..1ce8908824b 100644
+--- a/server/request.c
++++ b/server/request.c
+@@ -167,6 +167,8 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
+ int file_req = (r->main && r->filename);
+ int access_status;
+ core_dir_config *d;
++ core_server_config *sconf =
++ ap_get_core_module_config(r->server->module_config);
+
+ /* Ignore embedded %2F's in path for proxy requests */
+ if (!r->proxyreq && r->parsed_uri.path) {
+@@ -191,6 +193,10 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
+ }
+
+ ap_getparents(r->uri); /* OK --- shrinking transformations... */
++ if (sconf->merge_slashes != AP_CORE_CONFIG_OFF) {
++ ap_no2slash(r->uri);
++ ap_no2slash(r->parsed_uri.path);
++ }
+
+ /* All file subrequests are a huge pain... they cannot bubble through the
+ * next several steps. Only file subrequests are allowed an empty uri,
+@@ -1411,20 +1417,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
+
+ cache = prep_walk_cache(AP_NOTE_LOCATION_WALK, r);
+ cached = (cache->cached != NULL);
+-
+- /* Location and LocationMatch differ on their behaviour w.r.t. multiple
+- * slashes. Location matches multiple slashes with a single slash,
+- * LocationMatch doesn't. An exception, for backwards brokenness is
+- * absoluteURIs... in which case neither match multiple slashes.
+- */
+- if (r->uri[0] != '/') {
+- entry_uri = r->uri;
+- }
+- else {
+- char *uri = apr_pstrdup(r->pool, r->uri);
+- ap_no2slash(uri);
+- entry_uri = uri;
+- }
++ entry_uri = r->uri;
+
+ /* If we have an cache->cached location that matches r->uri,
+ * and the vhost's list of locations hasn't changed, we can skip
+@@ -1491,7 +1484,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
+ pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t));
+ }
+
+- if (ap_regexec(entry_core->r, r->uri, nmatch, pmatch, 0)) {
++ if (ap_regexec(entry_core->r, entry_uri, nmatch, pmatch, 0)) {
+ continue;
+ }
+
+@@ -1501,7 +1494,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
+ apr_table_setn(r->subprocess_env,
+ ((const char **)entry_core->refs->elts)[i],
+ apr_pstrndup(r->pool,
+- r->uri + pmatch[i].rm_so,
++ entry_uri + pmatch[i].rm_so,
+ pmatch[i].rm_eo - pmatch[i].rm_so));
+ }
+ }
+diff --git a/server/util.c b/server/util.c
+index fd7a0a14763..607c4850d86 100644
+--- a/server/util.c
++++ b/server/util.c
+@@ -561,16 +561,16 @@ AP_DECLARE(void) ap_getparents(char *name)
+ name[l] = '\0';
+ }
+ }
+-
+-AP_DECLARE(void) ap_no2slash(char *name)
++AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path)
+ {
++
+ char *d, *s;
+
+ s = d = name;
+
+ #ifdef HAVE_UNC_PATHS
+ /* Check for UNC names. Leave leading two slashes. */
+- if (s[0] == '/' && s[1] == '/')
++ if (is_fs_path && s[0] == '/' && s[1] == '/')
+ *d++ = *s++;
+ #endif
+
+@@ -587,6 +587,10 @@ AP_DECLARE(void) ap_no2slash(char *name)
+ *d = '\0';
+ }
+
++AP_DECLARE(void) ap_no2slash(char *name)
++{
++ ap_no2slash_ex(name, 1);
++}
+
+ /*
+ * copy at most n leading directories of s into d
diff --git a/CVE-2019-0220-2.patch b/CVE-2019-0220-2.patch
new file mode 100644
index 0000000..0204259
--- /dev/null
+++ b/CVE-2019-0220-2.patch
@@ -0,0 +1,50 @@
+From c4ef468b25718a26f2b92cbea3ca093729b79331 Mon Sep 17 00:00:00 2001
+From: Eric Covener
+Date: Mon, 18 Mar 2019 12:10:15 +0000
+Subject: [PATCH] merge 1855743,1855744 ^/httpd/httpd/trunk .
+
+r->parsed_uri.path safety in recent backport
+
+*) core: fix SEGFAULT in CONNECT with recent change
+ 2.4.x: svn merge -c 1855743,1855744 ^/httpd/httpd/trunk .
+ +1: rpluem, icing, covener
+
+
+
+
+git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1855751 13f79535-47bb-0310-9956-ffa450edef68
+---
+ server/request.c | 4 +++-
+ server/util.c | 4 ++++
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/server/request.c b/server/request.c
+index 1ce8908824b..d5c558afa30 100644
+--- a/server/request.c
++++ b/server/request.c
+@@ -195,7 +195,9 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
+ ap_getparents(r->uri); /* OK --- shrinking transformations... */
+ if (sconf->merge_slashes != AP_CORE_CONFIG_OFF) {
+ ap_no2slash(r->uri);
+- ap_no2slash(r->parsed_uri.path);
++ if (r->parsed_uri.path) {
++ ap_no2slash(r->parsed_uri.path);
++ }
+ }
+
+ /* All file subrequests are a huge pain... they cannot bubble through the
+diff --git a/server/util.c b/server/util.c
+index 607c4850d86..f3b17f1581e 100644
+--- a/server/util.c
++++ b/server/util.c
+@@ -566,6 +566,10 @@ AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path)
+
+ char *d, *s;
+
++ if (!name || !*name) {
++ return;
++ }
++
+ s = d = name;
+
+ #ifdef HAVE_UNC_PATHS
diff --git a/CVE-2019-0220-3.patch b/CVE-2019-0220-3.patch
new file mode 100644
index 0000000..7b3ff6f
--- /dev/null
+++ b/CVE-2019-0220-3.patch
@@ -0,0 +1,43 @@
+From 3451fc2bf8708b0dc8cd6a7d0ac0fe5b6401befc Mon Sep 17 00:00:00 2001
+From: Eric Covener
+Date: Tue, 19 Mar 2019 18:01:21 +0000
+Subject: [PATCH] *) maintainer mode fix for util.c no2slash_ex trunk
+ patch: http://svn.apache.org/r1855755 2.4.x patch svn merge -c 1855755
+ ^/httpd/httpd/trunk . +1: covener, rpluem, jim, ylavic
+
+git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1855853 13f79535-47bb-0310-9956-ffa450edef68
+---
+ STATUS | 6 ------
+ server/util.c | 2 +-
+ 2 files changed, 1 insertion(+), 7 deletions(-)
+
+#diff --git a/STATUS b/STATUS
+#index ffe5d22550c..1f8cb2f7884 100644
+#--- a/STATUS
+#+++ b/STATUS
+#@@ -126,12 +126,6 @@ RELEASE SHOWSTOPPERS:
+# PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
+# [ start all new proposals below, under PATCHES PROPOSED. ]
+#
+#- *) maintainer mode fix for util.c no2slash_ex
+#- trunk patch: http://svn.apache.org/r1855755
+#- 2.4.x patch svn merge -c 1855755 ^/httpd/httpd/trunk .
+#- +1: covener, rpluem, jim, ylavic
+#-
+#-
+# PATCHES PROPOSED TO BACKPORT FROM TRUNK:
+# [ New proposals should be added at the end of the list ]
+#
+diff --git a/server/util.c b/server/util.c
+index f3b17f1581e..e0c558cee2d 100644
+--- a/server/util.c
++++ b/server/util.c
+@@ -566,7 +566,7 @@ AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path)
+
+ char *d, *s;
+
+- if (!name || !*name) {
++ if (!*name) {
+ return;
+ }
+
diff --git a/MPMs-Initialize-all-runtime-asynchronous-objects-on-.patch b/MPMs-Initialize-all-runtime-asynchronous-objects-on-.patch
new file mode 100644
index 0000000..b41aa08
--- /dev/null
+++ b/MPMs-Initialize-all-runtime-asynchronous-objects-on-.patch
@@ -0,0 +1,699 @@
+From 98928d02d2473ceb9f81b1c3bc527f8b0a0039e6 Mon Sep 17 00:00:00 2001
+From: Graham Leggett
+Date: Fri, 21 Sep 2018 13:30:15 +0000
+Subject: [PATCH 188/504] MPMs: Initialize all runtime/asynchronous objects on
+ a dedicated pool and before signals handling to avoid lifetime issues on
+ restart or shutdown. PR 62658. trunk patch: http://svn.apache.org/r1835845
+ http://svn.apache.org/r1835846
+ http://svn.apache.org/r1837354 http://svn.apache.org/r1837356
+ http://svn.apache.org/r1839571
+ http://svn.apache.org/r1839583 2.4.x patch:
+ http://home.apache.org/~ylavic/patches/2.4.x-mpms_async_objects_lifetime.patch
+ +1: ylavic, jim (but not for 2.4.35), minfrin
+
+git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1841586 13f79535-47bb-0310-9956-ffa450edef68
+---
+ CHANGES | 4 +
+ STATUS | 11 --
+ server/mpm/event/event.c | 203 +++++++++++++++------------
+ server/mpm/mpmt_os2/mpmt_os2_child.c | 1 +
+ server/mpm/netware/mpm_netware.c | 1 +
+ server/mpm/prefork/prefork.c | 6 +-
+ server/mpm/winnt/child.c | 3 +
+ server/mpm/winnt/mpm_winnt.c | 1 +
+ server/mpm/worker/worker.c | 148 +++++++++++--------
+ 9 files changed, 215 insertions(+), 163 deletions(-)
+
+diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c
+index f07b757ab9..ffe8a23cbd 100644
+--- a/server/mpm/event/event.c
++++ b/server/mpm/event/event.c
+@@ -436,6 +436,7 @@ int raise_sigstop_flags;
+
+ static apr_pool_t *pconf; /* Pool for config stuff */
+ static apr_pool_t *pchild; /* Pool for httpd child stuff */
++static apr_pool_t *pruntime; /* Pool for MPM threads stuff */
+
+ static pid_t ap_my_pid; /* Linux getpid() doesn't work except in main
+ thread. Use this instead */
+@@ -709,10 +710,10 @@ static void event_note_child_killed(int childnum, pid_t pid, ap_generation_t gen
+
+ static void event_note_child_started(int slot, pid_t pid)
+ {
++ ap_generation_t gen = retained->mpm->my_generation;
+ ap_scoreboard_image->parent[slot].pid = pid;
+- ap_run_child_status(ap_server_conf,
+- ap_scoreboard_image->parent[slot].pid,
+- retained->mpm->my_generation, slot, MPM_CHILD_STARTED);
++ ap_scoreboard_image->parent[slot].generation = gen;
++ ap_run_child_status(ap_server_conf, pid, gen, slot, MPM_CHILD_STARTED);
+ }
+
+ static const char *event_get_name(void)
+@@ -1270,36 +1271,6 @@ static void dummy_signal_handler(int sig)
+ }
+
+
+-static apr_status_t init_pollset(apr_pool_t *p)
+-{
+- ap_listen_rec *lr;
+- listener_poll_type *pt;
+- int i = 0;
+-
+- listener_pollfd = apr_palloc(p, sizeof(apr_pollfd_t) * num_listensocks);
+- for (lr = my_bucket->listeners; lr != NULL; lr = lr->next, i++) {
+- apr_pollfd_t *pfd;
+- AP_DEBUG_ASSERT(i < num_listensocks);
+- pfd = &listener_pollfd[i];
+- pt = apr_pcalloc(p, sizeof(*pt));
+- pfd->desc_type = APR_POLL_SOCKET;
+- pfd->desc.s = lr->sd;
+- pfd->reqevents = APR_POLLIN;
+-
+- pt->type = PT_ACCEPT;
+- pt->baton = lr;
+-
+- pfd->client_data = pt;
+-
+- apr_socket_opt_set(pfd->desc.s, APR_SO_NONBLOCK, 1);
+- apr_pollset_add(event_pollset, pfd);
+-
+- lr->accept_func = ap_unixd_accept;
+- }
+-
+- return APR_SUCCESS;
+-}
+-
+ static apr_status_t push_timer2worker(timer_event_t* te)
+ {
+ return ap_queue_push_timer(worker_queue, te);
+@@ -1611,7 +1582,6 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
+ proc_info *ti = dummy;
+ int process_slot = ti->pslot;
+ struct process_score *ps = ap_get_scoreboard_process(process_slot);
+- apr_pool_t *tpool = apr_thread_pool_get(thd);
+ int closed = 0;
+ int have_idle_worker = 0;
+ apr_time_t last_log;
+@@ -1619,16 +1589,6 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
+ last_log = apr_time_now();
+ free(ti);
+
+- rc = init_pollset(tpool);
+- if (rc != APR_SUCCESS) {
+- ap_log_error(APLOG_MARK, APLOG_ERR, rc, ap_server_conf,
+- "failed to initialize pollset, "
+- "shutdown process now");
+- resource_shortage = 1;
+- signal_threads(ST_UNGRACEFUL);
+- return NULL;
+- }
+-
+ /* Unblock the signal used to wake this thread up, and set a handler for
+ * it.
+ */
+@@ -2168,8 +2128,6 @@ static int check_signal(int signum)
+ return 0;
+ }
+
+-
+-
+ static void create_listener_thread(thread_starter * ts)
+ {
+ int my_child_num = ts->child_num_arg;
+@@ -2181,7 +2139,7 @@ static void create_listener_thread(thread_starter * ts)
+ my_info->pslot = my_child_num;
+ my_info->tslot = -1; /* listener thread doesn't have a thread slot */
+ rv = apr_thread_create(&ts->listener, thread_attr, listener_thread,
+- my_info, pchild);
++ my_info, pruntime);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(00474)
+ "apr_thread_create: unable to create listener thread");
+@@ -2191,25 +2149,12 @@ static void create_listener_thread(thread_starter * ts)
+ apr_os_thread_get(&listener_os_thread, ts->listener);
+ }
+
+-/* XXX under some circumstances not understood, children can get stuck
+- * in start_threads forever trying to take over slots which will
+- * never be cleaned up; for now there is an APLOG_DEBUG message issued
+- * every so often when this condition occurs
+- */
+-static void *APR_THREAD_FUNC start_threads(apr_thread_t * thd, void *dummy)
++static void setup_threads_runtime(void)
+ {
+- thread_starter *ts = dummy;
+- apr_thread_t **threads = ts->threads;
+- apr_threadattr_t *thread_attr = ts->threadattr;
+- int my_child_num = ts->child_num_arg;
+- proc_info *my_info;
+ apr_status_t rv;
+- int i;
+- int threads_created = 0;
+- int listener_started = 0;
+- int loops;
+- int prev_threads_created;
+- int max_recycled_pools = -1;
++ ap_listen_rec *lr;
++ apr_pool_t *pskip = NULL;
++ int max_recycled_pools = -1, i;
+ const int good_methods[] = { APR_POLLSET_KQUEUE,
+ APR_POLLSET_PORT,
+ APR_POLLSET_EPOLL };
+@@ -2218,10 +2163,39 @@ static void *APR_THREAD_FUNC start_threads(apr_thread_t * thd, void *dummy)
+ const apr_uint32_t pollset_size = (apr_uint32_t)num_listensocks +
+ (apr_uint32_t)threads_per_child *
+ (async_factor > 2 ? async_factor : 2);
++ int pollset_flags;
++
++ /* Event's skiplist operations will happen concurrently with other modules'
++ * runtime so they need their own pool for allocations, and its lifetime
++ * should be at least the one of the connections (ptrans). Thus pskip is
++ * created as a subpool of pconf like/before ptrans (before so that it's
++ * destroyed after). In forked mode pconf is never destroyed so we are good
++ * anyway, but in ONE_PROCESS mode this ensures that the skiplist works
++ * from connection/ptrans cleanups (even after pchild is destroyed).
++ */
++ apr_pool_create(&pskip, pconf);
++ apr_pool_tag(pskip, "mpm_skiplist");
++ apr_thread_mutex_create(&g_timer_skiplist_mtx, APR_THREAD_MUTEX_DEFAULT, pskip);
++ APR_RING_INIT(&timer_free_ring, timer_event_t, link);
++ apr_skiplist_init(&timer_skiplist, pskip);
++ apr_skiplist_set_compare(timer_skiplist, timer_comp, timer_comp);
++
++ /* All threads (listener, workers) and synchronization objects (queues,
++ * pollset, mutexes...) created here should have at least the lifetime of
++ * the connections they handle (i.e. ptrans). We can't use this thread's
++ * self pool because all these objects survive it, nor use pchild or pconf
++ * directly because this starter thread races with other modules' runtime,
++ * nor finally pchild (or subpool thereof) because it is killed explicitely
++ * before pconf (thus connections/ptrans can live longer, which matters in
++ * ONE_PROCESS mode). So this leaves us with a subpool of pconf, created
++ * before any ptrans hence destroyed after.
++ */
++ apr_pool_create(&pruntime, pconf);
++ apr_pool_tag(pruntime, "mpm_runtime");
+
+ /* We must create the fd queues before we start up the listener
+ * and worker threads. */
+- rv = ap_queue_create(&worker_queue, threads_per_child, pchild);
++ rv = ap_queue_create(&worker_queue, threads_per_child, pruntime);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(03100)
+ "ap_queue_create() failed");
+@@ -2235,7 +2209,7 @@ static void *APR_THREAD_FUNC start_threads(apr_thread_t * thd, void *dummy)
+ */
+ max_recycled_pools = threads_per_child * 3 / 4 ;
+ }
+- rv = ap_queue_info_create(&worker_queue_info, pchild,
++ rv = ap_queue_info_create(&worker_queue_info, pruntime,
+ threads_per_child, max_recycled_pools);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(03101)
+@@ -2247,7 +2221,7 @@ static void *APR_THREAD_FUNC start_threads(apr_thread_t * thd, void *dummy)
+ * thread starts.
+ */
+ rv = apr_thread_mutex_create(&timeout_mutex, APR_THREAD_MUTEX_DEFAULT,
+- pchild);
++ pruntime);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(03102)
+ "creation of the timeout mutex failed.");
+@@ -2255,25 +2229,30 @@ static void *APR_THREAD_FUNC start_threads(apr_thread_t * thd, void *dummy)
+ }
+
+ /* Create the main pollset */
++ pollset_flags = APR_POLLSET_THREADSAFE | APR_POLLSET_NOCOPY |
++ APR_POLLSET_NODEFAULT | APR_POLLSET_WAKEABLE;
+ for (i = 0; i < sizeof(good_methods) / sizeof(good_methods[0]); i++) {
+- apr_uint32_t flags = APR_POLLSET_THREADSAFE | APR_POLLSET_NOCOPY |
+- APR_POLLSET_NODEFAULT | APR_POLLSET_WAKEABLE;
+- rv = apr_pollset_create_ex(&event_pollset, pollset_size, pchild, flags,
+- good_methods[i]);
++ rv = apr_pollset_create_ex(&event_pollset, pollset_size, pruntime,
++ pollset_flags, good_methods[i]);
+ if (rv == APR_SUCCESS) {
+ listener_is_wakeable = 1;
+ break;
+ }
+- flags &= ~APR_POLLSET_WAKEABLE;
+- rv = apr_pollset_create_ex(&event_pollset, pollset_size, pchild, flags,
+- good_methods[i]);
+- if (rv == APR_SUCCESS) {
+- break;
++ }
++ if (rv != APR_SUCCESS) {
++ pollset_flags &= ~APR_POLLSET_WAKEABLE;
++ for (i = 0; i < sizeof(good_methods) / sizeof(good_methods[0]); i++) {
++ rv = apr_pollset_create_ex(&event_pollset, pollset_size, pruntime,
++ pollset_flags, good_methods[i]);
++ if (rv == APR_SUCCESS) {
++ break;
++ }
+ }
+ }
+ if (rv != APR_SUCCESS) {
+- rv = apr_pollset_create(&event_pollset, pollset_size, pchild,
+- APR_POLLSET_THREADSAFE | APR_POLLSET_NOCOPY);
++ pollset_flags &= ~APR_POLLSET_NODEFAULT;
++ rv = apr_pollset_create(&event_pollset, pollset_size, pruntime,
++ pollset_flags);
+ }
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(03103)
+@@ -2281,12 +2260,57 @@ static void *APR_THREAD_FUNC start_threads(apr_thread_t * thd, void *dummy)
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
++ /* Add listeners to the main pollset */
++ listener_pollfd = apr_pcalloc(pruntime, num_listensocks *
++ sizeof(apr_pollfd_t));
++ for (i = 0, lr = my_bucket->listeners; lr; lr = lr->next, i++) {
++ apr_pollfd_t *pfd;
++ listener_poll_type *pt;
++
++ AP_DEBUG_ASSERT(i < num_listensocks);
++ pfd = &listener_pollfd[i];
++
++ pfd->reqevents = APR_POLLIN;
++ pfd->desc_type = APR_POLL_SOCKET;
++ pfd->desc.s = lr->sd;
++
++ pt = apr_pcalloc(pruntime, sizeof(*pt));
++ pfd->client_data = pt;
++ pt->type = PT_ACCEPT;
++ pt->baton = lr;
++
++ apr_socket_opt_set(pfd->desc.s, APR_SO_NONBLOCK, 1);
++ apr_pollset_add(event_pollset, pfd);
++
++ lr->accept_func = ap_unixd_accept;
++ }
++
++ worker_sockets = apr_pcalloc(pruntime, threads_per_child *
++ sizeof(apr_socket_t *));
++}
++
++/* XXX under some circumstances not understood, children can get stuck
++ * in start_threads forever trying to take over slots which will
++ * never be cleaned up; for now there is an APLOG_DEBUG message issued
++ * every so often when this condition occurs
++ */
++static void *APR_THREAD_FUNC start_threads(apr_thread_t * thd, void *dummy)
++{
++ thread_starter *ts = dummy;
++ apr_thread_t **threads = ts->threads;
++ apr_threadattr_t *thread_attr = ts->threadattr;
++ int my_child_num = ts->child_num_arg;
++ proc_info *my_info;
++ apr_status_t rv;
++ int threads_created = 0;
++ int listener_started = 0;
++ int prev_threads_created;
++ int loops, i;
++
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02471)
+ "start_threads: Using %s (%swakeable)",
+ apr_pollset_method_name(event_pollset),
+ listener_is_wakeable ? "" : "not ");
+- worker_sockets = apr_pcalloc(pchild, threads_per_child
+- * sizeof(apr_socket_t *));
+
+ loops = prev_threads_created = 0;
+ while (1) {
+@@ -2310,7 +2334,7 @@ static void *APR_THREAD_FUNC start_threads(apr_thread_t * thd, void *dummy)
+ * done because it lets us deal with tid better.
+ */
+ rv = apr_thread_create(&threads[i], thread_attr,
+- worker_thread, my_info, pchild);
++ worker_thread, my_info, pruntime);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
+ APLOGNO(03104)
+@@ -2431,7 +2455,6 @@ static void child_main(int child_num_arg, int child_bucket)
+ apr_threadattr_t *thread_attr;
+ apr_thread_t *start_thread_id;
+ int i;
+- apr_pool_t *pskip;
+
+ /* for benefit of any hooks that run as this child initializes */
+ retained->mpm->mpm_state = AP_MPMQ_STARTING;
+@@ -2439,7 +2462,12 @@ static void child_main(int child_num_arg, int child_bucket)
+ ap_my_pid = getpid();
+ ap_child_slot = child_num_arg;
+ ap_fatal_signal_child_setup(ap_server_conf);
++
++ /* Get a sub context for global allocations in this child, so that
++ * we can have cleanups occur when the child exits.
++ */
+ apr_pool_create(&pchild, pconf);
++ apr_pool_tag(pchild, "pchild");
+
+ /* close unused listeners and pods */
+ for (i = 0; i < retained->mpm->num_buckets; i++) {
+@@ -2457,12 +2485,6 @@ static void child_main(int child_num_arg, int child_bucket)
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+- apr_thread_mutex_create(&g_timer_skiplist_mtx, APR_THREAD_MUTEX_DEFAULT, pchild);
+- APR_RING_INIT(&timer_free_ring, timer_event_t, link);
+- apr_pool_create(&pskip, pchild);
+- apr_skiplist_init(&timer_skiplist, pskip);
+- apr_skiplist_set_compare(timer_skiplist, timer_comp, timer_comp);
+-
+ /* Just use the standard apr_setup_signal_thread to block all signals
+ * from being received. The child processes no longer use signals for
+ * any communication with the parent process. Let's also do this before
+@@ -2486,7 +2508,10 @@ static void child_main(int child_num_arg, int child_bucket)
+ conns_this_child = APR_INT32_MAX;
+ }
+
+- /* Setup worker threads */
++ /* Setup threads */
++
++ /* Globals used by signal_threads() so to be initialized before */
++ setup_threads_runtime();
+
+ /* clear the storage; we may not create all our threads immediately,
+ * and we want a 0 entry to indicate a thread which was not created
+diff --git a/server/mpm/mpmt_os2/mpmt_os2_child.c b/server/mpm/mpmt_os2/mpmt_os2_child.c
+index ca9f594754..bb7e1369ea 100644
+--- a/server/mpm/mpmt_os2/mpmt_os2_child.c
++++ b/server/mpm/mpmt_os2/mpmt_os2_child.c
+@@ -110,6 +110,7 @@ void ap_mpm_child_main(apr_pool_t *pconf)
+
+ /* Create pool for child */
+ apr_pool_create(&pchild, pconf);
++ apr_pool_tag(pchild, "pchild");
+
+ ap_run_child_init(pchild, ap_server_conf);
+
+diff --git a/server/mpm/netware/mpm_netware.c b/server/mpm/netware/mpm_netware.c
+index 2fab52f598..82480334b8 100644
+--- a/server/mpm/netware/mpm_netware.c
++++ b/server/mpm/netware/mpm_netware.c
+@@ -886,6 +886,7 @@ static int netware_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
+
+ /* Only set slot 0 since that is all NetWare will ever have. */
+ ap_scoreboard_image->parent[0].pid = getpid();
++ ap_scoreboard_image->parent[0].generation = ap_my_generation;
+ ap_run_child_status(ap_server_conf,
+ ap_scoreboard_image->parent[0].pid,
+ ap_my_generation,
+diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c
+index 3fb328467d..8efda72ee1 100644
+--- a/server/mpm/prefork/prefork.c
++++ b/server/mpm/prefork/prefork.c
+@@ -208,10 +208,10 @@ static void prefork_note_child_killed(int childnum, pid_t pid,
+
+ static void prefork_note_child_started(int slot, pid_t pid)
+ {
++ ap_generation_t gen = retained->mpm->my_generation;
+ ap_scoreboard_image->parent[slot].pid = pid;
+- ap_run_child_status(ap_server_conf,
+- ap_scoreboard_image->parent[slot].pid,
+- retained->mpm->my_generation, slot, MPM_CHILD_STARTED);
++ ap_scoreboard_image->parent[slot].generation = gen;
++ ap_run_child_status(ap_server_conf, pid, gen, slot, MPM_CHILD_STARTED);
+ }
+
+ /* a clean exit from a child with proper cleanup */
+diff --git a/server/mpm/winnt/child.c b/server/mpm/winnt/child.c
+index 9ddba18f80..21755f398c 100644
+--- a/server/mpm/winnt/child.c
++++ b/server/mpm/winnt/child.c
+@@ -917,6 +917,9 @@ void child_main(apr_pool_t *pconf, DWORD parent_pid)
+ int i;
+ int num_events;
+
++ /* Get a sub context for global allocations in this child, so that
++ * we can have cleanups occur when the child exits.
++ */
+ apr_pool_create(&pchild, pconf);
+ apr_pool_tag(pchild, "pchild");
+
+diff --git a/server/mpm/winnt/mpm_winnt.c b/server/mpm/winnt/mpm_winnt.c
+index 2487e45be2..3d71f80e72 100644
+--- a/server/mpm/winnt/mpm_winnt.c
++++ b/server/mpm/winnt/mpm_winnt.c
+@@ -139,6 +139,7 @@ AP_INIT_TAKE1("ThreadLimit", set_thread_limit, NULL, RSRC_CONF,
+ static void winnt_note_child_started(int slot, pid_t pid)
+ {
+ ap_scoreboard_image->parent[slot].pid = pid;
++ ap_scoreboard_image->parent[slot].generation = my_generation;
+ ap_run_child_status(ap_server_conf,
+ ap_scoreboard_image->parent[slot].pid,
+ my_generation, slot, MPM_CHILD_STARTED);
+diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c
+index 7804efc457..8012fe29d8 100644
+--- a/server/mpm/worker/worker.c
++++ b/server/mpm/worker/worker.c
+@@ -134,6 +134,8 @@ static int num_listensocks = 0;
+ static int resource_shortage = 0;
+ static fd_queue_t *worker_queue;
+ static fd_queue_info_t *worker_queue_info;
++static apr_pollset_t *worker_pollset;
++
+
+ /* data retained by worker across load/unload of the module
+ * allocated on first call to pre-config hook; located on
+@@ -218,6 +220,7 @@ int raise_sigstop_flags;
+
+ static apr_pool_t *pconf; /* Pool for config stuff */
+ static apr_pool_t *pchild; /* Pool for httpd child stuff */
++static apr_pool_t *pruntime; /* Pool for MPM threads stuff */
+
+ static pid_t ap_my_pid; /* Linux getpid() doesn't work except in main
+ thread. Use this instead */
+@@ -392,10 +395,10 @@ static void worker_note_child_killed(int childnum, pid_t pid, ap_generation_t ge
+
+ static void worker_note_child_started(int slot, pid_t pid)
+ {
++ ap_generation_t gen = retained->mpm->my_generation;
+ ap_scoreboard_image->parent[slot].pid = pid;
+- ap_run_child_status(ap_server_conf,
+- ap_scoreboard_image->parent[slot].pid,
+- retained->mpm->my_generation, slot, MPM_CHILD_STARTED);
++ ap_scoreboard_image->parent[slot].generation = gen;
++ ap_run_child_status(ap_server_conf, pid, gen, slot, MPM_CHILD_STARTED);
+ }
+
+ static void worker_note_child_lost_slot(int slot, pid_t newpid)
+@@ -538,47 +541,15 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy)
+ {
+ proc_info * ti = dummy;
+ int process_slot = ti->pid;
+- apr_pool_t *tpool = apr_thread_pool_get(thd);
+ void *csd = NULL;
+ apr_pool_t *ptrans = NULL; /* Pool for per-transaction stuff */
+- apr_pollset_t *pollset;
+ apr_status_t rv;
+- ap_listen_rec *lr;
++ ap_listen_rec *lr = NULL;
+ int have_idle_worker = 0;
+ int last_poll_idx = 0;
+
+ free(ti);
+
+- rv = apr_pollset_create(&pollset, num_listensocks, tpool,
+- APR_POLLSET_NOCOPY);
+- if (rv != APR_SUCCESS) {
+- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+- "Couldn't create pollset in thread;"
+- " check system or user limits");
+- /* let the parent decide how bad this really is */
+- clean_child_exit(APEXIT_CHILDSICK);
+- }
+-
+- for (lr = my_bucket->listeners; lr != NULL; lr = lr->next) {
+- apr_pollfd_t *pfd = apr_pcalloc(tpool, sizeof *pfd);
+-
+- pfd->desc_type = APR_POLL_SOCKET;
+- pfd->desc.s = lr->sd;
+- pfd->reqevents = APR_POLLIN;
+- pfd->client_data = lr;
+-
+- rv = apr_pollset_add(pollset, pfd);
+- if (rv != APR_SUCCESS) {
+- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+- "Couldn't create add listener to pollset;"
+- " check system or user limits");
+- /* let the parent decide how bad this really is */
+- clean_child_exit(APEXIT_CHILDSICK);
+- }
+-
+- lr->accept_func = ap_unixd_accept;
+- }
+-
+ /* Unblock the signal used to wake this thread up, and set a handler for
+ * it.
+ */
+@@ -630,7 +601,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy)
+ apr_int32_t numdesc;
+ const apr_pollfd_t *pdesc;
+
+- rv = apr_pollset_poll(pollset, -1, &numdesc, &pdesc);
++ rv = apr_pollset_poll(worker_pollset, -1, &numdesc, &pdesc);
+ if (rv != APR_SUCCESS) {
+ if (APR_STATUS_IS_EINTR(rv)) {
+ continue;
+@@ -871,7 +842,7 @@ static void create_listener_thread(thread_starter *ts)
+ my_info->tid = -1; /* listener thread doesn't have a thread slot */
+ my_info->sd = 0;
+ rv = apr_thread_create(&ts->listener, thread_attr, listener_thread,
+- my_info, pchild);
++ my_info, pruntime);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(00275)
+ "apr_thread_create: unable to create listener thread");
+@@ -881,35 +852,34 @@ static void create_listener_thread(thread_starter *ts)
+ apr_os_thread_get(&listener_os_thread, ts->listener);
+ }
+
+-/* XXX under some circumstances not understood, children can get stuck
+- * in start_threads forever trying to take over slots which will
+- * never be cleaned up; for now there is an APLOG_DEBUG message issued
+- * every so often when this condition occurs
+- */
+-static void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy)
++static void setup_threads_runtime(void)
+ {
+- thread_starter *ts = dummy;
+- apr_thread_t **threads = ts->threads;
+- apr_threadattr_t *thread_attr = ts->threadattr;
+- int my_child_num = ts->child_num_arg;
+- proc_info *my_info;
++ ap_listen_rec *lr;
+ apr_status_t rv;
+- int i;
+- int threads_created = 0;
+- int listener_started = 0;
+- int loops;
+- int prev_threads_created;
++
++ /* All threads (listener, workers) and synchronization objects (queues,
++ * pollset, mutexes...) created here should have at least the lifetime of
++ * the connections they handle (i.e. ptrans). We can't use this thread's
++ * self pool because all these objects survive it, nor use pchild or pconf
++ * directly because this starter thread races with other modules' runtime,
++ * nor finally pchild (or subpool thereof) because it is killed explicitely
++ * before pconf (thus connections/ptrans can live longer, which matters in
++ * ONE_PROCESS mode). So this leaves us with a subpool of pconf, created
++ * before any ptrans hence destroyed after.
++ */
++ apr_pool_create(&pruntime, pconf);
++ apr_pool_tag(pruntime, "mpm_runtime");
+
+ /* We must create the fd queues before we start up the listener
+ * and worker threads. */
+- rv = ap_queue_create(&worker_queue, threads_per_child, pchild);
++ rv = ap_queue_create(&worker_queue, threads_per_child, pruntime);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(03140)
+ "ap_queue_create() failed");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+- rv = ap_queue_info_create(&worker_queue_info, pchild,
++ rv = ap_queue_info_create(&worker_queue_info, pruntime,
+ threads_per_child, -1);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(03141)
+@@ -917,8 +887,58 @@ static void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy)
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+- worker_sockets = apr_pcalloc(pchild, threads_per_child
+- * sizeof(apr_socket_t *));
++ /* Create the main pollset */
++ rv = apr_pollset_create(&worker_pollset, num_listensocks, pruntime,
++ APR_POLLSET_NOCOPY);
++ if (rv != APR_SUCCESS) {
++ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, APLOGNO(03285)
++ "Couldn't create pollset in thread;"
++ " check system or user limits");
++ /* let the parent decide how bad this really is */
++ clean_child_exit(APEXIT_CHILDSICK);
++ }
++
++ for (lr = my_bucket->listeners; lr != NULL; lr = lr->next) {
++ apr_pollfd_t *pfd = apr_pcalloc(pruntime, sizeof *pfd);
++
++ pfd->desc_type = APR_POLL_SOCKET;
++ pfd->desc.s = lr->sd;
++ pfd->reqevents = APR_POLLIN;
++ pfd->client_data = lr;
++
++ rv = apr_pollset_add(worker_pollset, pfd);
++ if (rv != APR_SUCCESS) {
++ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, APLOGNO(03286)
++ "Couldn't create add listener to pollset;"
++ " check system or user limits");
++ /* let the parent decide how bad this really is */
++ clean_child_exit(APEXIT_CHILDSICK);
++ }
++
++ lr->accept_func = ap_unixd_accept;
++ }
++
++ worker_sockets = apr_pcalloc(pruntime, threads_per_child *
++ sizeof(apr_socket_t *));
++}
++
++/* XXX under some circumstances not understood, children can get stuck
++ * in start_threads forever trying to take over slots which will
++ * never be cleaned up; for now there is an APLOG_DEBUG message issued
++ * every so often when this condition occurs
++ */
++static void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy)
++{
++ thread_starter *ts = dummy;
++ apr_thread_t **threads = ts->threads;
++ apr_threadattr_t *thread_attr = ts->threadattr;
++ int my_child_num = ts->child_num_arg;
++ proc_info *my_info;
++ apr_status_t rv;
++ int threads_created = 0;
++ int listener_started = 0;
++ int prev_threads_created;
++ int loops, i;
+
+ loops = prev_threads_created = 0;
+ while (1) {
+@@ -942,7 +962,7 @@ static void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy)
+ * done because it lets us deal with tid better.
+ */
+ rv = apr_thread_create(&threads[i], thread_attr,
+- worker_thread, my_info, pchild);
++ worker_thread, my_info, pruntime);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(03142)
+ "apr_thread_create: unable to create worker thread");
+@@ -1082,7 +1102,12 @@ static void child_main(int child_num_arg, int child_bucket)
+
+ ap_my_pid = getpid();
+ ap_fatal_signal_child_setup(ap_server_conf);
++
++ /* Get a sub context for global allocations in this child, so that
++ * we can have cleanups occur when the child exits.
++ */
+ apr_pool_create(&pchild, pconf);
++ apr_pool_tag(pchild, "pchild");
+
+ /* close unused listeners and pods */
+ for (i = 0; i < retained->mpm->num_buckets; i++) {
+@@ -1132,7 +1157,10 @@ static void child_main(int child_num_arg, int child_bucket)
+ requests_this_child = INT_MAX;
+ }
+
+- /* Setup worker threads */
++ /* Setup threads */
++
++ /* Globals used by signal_threads() so to be initialized before */
++ setup_threads_runtime();
+
+ /* clear the storage; we may not create all our threads immediately,
+ * and we want a 0 entry to indicate a thread which was not created
+--
+2.19.1
+
diff --git a/Merge-of-r1853133-r1853166-from-trunk.patch b/Merge-of-r1853133-r1853166-from-trunk.patch
new file mode 100644
index 0000000..d33dcd3
--- /dev/null
+++ b/Merge-of-r1853133-r1853166-from-trunk.patch
@@ -0,0 +1,68 @@
+From d9f2c7df12a2e51ed78056e2bdc5714abf32390c Mon Sep 17 00:00:00 2001
+From: Stefan Eissing
+Date: Fri, 8 Feb 2019 09:01:42 +0000
+Subject: [PATCH 370/504] Merge of r1853133,r1853166 from trunk:
+
+mod_ssl: Don't unset FIPS mode on restart unless it's forced by
+ configuration (SSLFIPS on) and not active by default in OpenSSL. PR 63136.
+
+
+
+git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1853197 13f79535-47bb-0310-9956-ffa450edef68
+---
+ CHANGES | 4 +++-
+ modules/ssl/mod_ssl.c | 3 ---
+ modules/ssl/ssl_engine_init.c | 12 +++++++++++-
+ 3 files changed, 14 insertions(+), 5 deletions(-)
+
+diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c
+index 9fdf9e042e..4797c78bb9 100644
+--- a/modules/ssl/mod_ssl.c
++++ b/modules/ssl/mod_ssl.c
+@@ -331,9 +331,6 @@ static apr_status_t ssl_cleanup_pre_config(void *data)
+ /*
+ * Try to kill the internals of the SSL library.
+ */
+-#ifdef HAVE_FIPS
+- FIPS_mode_set(0);
+-#endif
+ /* Corresponds to OBJ_create()s */
+ OBJ_cleanup();
+ /* Corresponds to OPENSSL_load_builtin_modules() */
+diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
+index 18d18c691f..48d7b96cd8 100644
+--- a/modules/ssl/ssl_engine_init.c
++++ b/modules/ssl/ssl_engine_init.c
+@@ -183,6 +183,14 @@ int ssl_is_challenge(conn_rec *c, const char *servername,
+ return 0;
+ }
+
++#ifdef HAVE_FIPS
++static apr_status_t modssl_fips_cleanup(void *data)
++{
++ FIPS_mode_set(0);
++ return APR_SUCCESS;
++}
++#endif
++
+ /*
+ * Per-module initialization
+ */
+@@ -311,11 +319,13 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
+ ssl_rand_seed(base_server, ptemp, SSL_RSCTX_STARTUP, "Init: ");
+
+ #ifdef HAVE_FIPS
+- if(sc->fips) {
++ if (sc->fips) {
+ if (!FIPS_mode()) {
+ if (FIPS_mode_set(1)) {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(01884)
+ "Operating in SSL FIPS mode");
++ apr_pool_cleanup_register(p, NULL, modssl_fips_cleanup,
++ apr_pool_cleanup_null);
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01885) "FIPS mode failed");
+--
+2.19.1
+
diff --git a/Merge-r1418761-r1418765-r1510295-r1757147-r1805163-r.patch b/Merge-r1418761-r1418765-r1510295-r1757147-r1805163-r.patch
new file mode 100644
index 0000000..4e2ae48
--- /dev/null
+++ b/Merge-r1418761-r1418765-r1510295-r1757147-r1805163-r.patch
@@ -0,0 +1,397 @@
+From fd0648f226ecbc38917ccb076a2147e206d73c8b Mon Sep 17 00:00:00 2001
+From: Jim Jagielski
+Date: Wed, 15 Aug 2018 15:01:08 +0000
+Subject: [PATCH 075/504] Merge r1418761, r1418765, r1510295, r1757147,
+ r1805163, r1818924, r1827374, r1831772, r1832351, r1832951, r1815004 from
+ trunk:
+
+Don't claim "BIO dump follows" if it is not logged due to log level config.
+
+
+make ssl_io_data_dump respect per-conn loglevel
+
+
+add high trace level log messages for debugging buffering and write completion
+
+
+* modules/ssl/ssl_engine_kernel.c (ssl_callback_SessionTicket): Fail
+ if RAND_bytes() fails; possible per API, although not in practice
+ with the OpenSSL implementation.
+
+
+Fix typo in log message.
+
+
+ap_add_common_vars(): use apr_pstrmemdup().
+
+This avoids a transient replacement/restore of '?' by '\0' in r->filename.
+
+
+Use 'ap_request_has_body()' instead of duplicating its implemenation.
+
+The logic in 'ap_request_has_body()' is:
+ has_body = (!r->header_only
+ && (r->kept_body
+ || apr_table_get(r->headers_in, "Transfer-Encoding")
+ || ( (cls = apr_table_get(r->headers_in, "Content-Length"))
+ && (apr_strtoff(&cl, cls, &estr, 10) == APR_SUCCESS)
+ && (!*estr)
+ && (cl > 0) )
+ )
+ );
+So the test is slighly different from the original code. (but this looks fine to me)
+
+This also has the advantage to avoid a redundant call to 'apr_table_get()' and to improve readability.
+
+While at it, move the test '!r->expecting_100' a few lines above because it is cheap.
+
+PR62368: Print the unparsed URI in AH03454
+
+... to include r->args and get otherwise get as close to possible to
+what came in over the wire.
+
+Submitted By: Hank Ibell
+Committed By: covener
+
+
+
+
+All error handling paths of this function call 'apr_brigade_destroy()' , except this one.
+So add it here too.
+
+Probably spotted with the help of the Coccinelle software (Thx Julia for the patch and for Coccinelle)
+
+See PR 53016
+
+* modules/proxy/proxy_util.c (ap_proxy_share_worker): Skip creating subpool
+ for debugging unless debug-level logging is enabled. No functional change.
+
+
+mod_watchdog: Correct some log messages and fix
+compiler warning
+"'rv' may be used uninitialized in this function".
+
+Follow up to r1722154.
+
+Submitted by: sf, jorton, jorton, ylavic, jailletc36, covener, jailletc36, jorton, rjung
+Reviewed by: jailletc36, jim, jorton
+
+
+git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1838103 13f79535-47bb-0310-9956-ffa450edef68
+---
+ CHANGES | 2 ++
+ STATUS | 26 ---------------------
+ modules/core/mod_watchdog.c | 10 +++++----
+ modules/proxy/mod_proxy_ajp.c | 1 +
+ modules/proxy/proxy_util.c | 3 ++-
+ modules/ssl/ssl_engine_io.c | 31 ++++++++++++++++---------
+ modules/ssl/ssl_engine_kernel.c | 12 +++++-----
+ server/core_filters.c | 40 ++++++++++++++++++++++++++++++---
+ server/protocol.c | 2 +-
+ server/util_script.c | 5 ++---
+ 10 files changed, 77 insertions(+), 55 deletions(-)
+
+diff --git a/modules/core/mod_watchdog.c b/modules/core/mod_watchdog.c
+index b6deaba306..61f4675252 100644
+--- a/modules/core/mod_watchdog.c
++++ b/modules/core/mod_watchdog.c
+@@ -534,11 +534,13 @@ static int wd_post_config_hook(apr_pool_t *pconf, apr_pool_t *plog,
+ w->name, s,
+ wd_server_conf->pool, 0);
+ if (rv != APR_SUCCESS) {
++ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(10095)
++ "Watchdog: Failed to create singleton mutex.");
+ return rv;
+ }
++ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(02979)
++ "Watchdog: Created singleton mutex (%s).", w->name);
+ }
+- ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(02979)
+- "Watchdog: Created child worker thread (%s).", w->name);
+ wd_server_conf->child_workers++;
+ }
+ }
+@@ -580,12 +582,12 @@ static void wd_child_init_hook(apr_pool_t *p, server_rec *s)
+ */
+ if ((rv = wd_startup(w, wd_server_conf->pool)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(01573)
+- "Watchdog: Failed to create worker thread.");
++ "Watchdog: Failed to create child worker thread.");
+ /* No point to continue */
+ return;
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(02981)
+- "Watchdog: Created worker thread (%s).", wn[i].provider_name);
++ "Watchdog: Created child worker thread (%s).", wn[i].provider_name);
+ }
+ }
+ }
+diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c
+index 8669db6308..73716aff51 100644
+--- a/modules/proxy/mod_proxy_ajp.c
++++ b/modules/proxy/mod_proxy_ajp.c
+@@ -322,6 +322,7 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
+ * Close it to clean things up.
+ */
+ conn->close = 1;
++ apr_brigade_destroy(input_brigade);
+ return HTTP_BAD_REQUEST;
+ }
+ }
+diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
+index 7b76144ba7..2bd0edbfbc 100644
+--- a/modules/proxy/proxy_util.c
++++ b/modules/proxy/proxy_util.c
+@@ -1879,7 +1879,8 @@ PROXY_DECLARE(apr_status_t) ap_proxy_share_worker(proxy_worker *worker, proxy_wo
+ }
+ worker->s = shm;
+ worker->s->index = i;
+- {
++
++ if (APLOGdebug(ap_server_conf)) {
+ apr_pool_t *pool;
+ apr_pool_create(&pool, ap_server_conf->process->pool);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02338)
+diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c
+index 1a47b0e982..d52d5e30ca 100644
+--- a/modules/ssl/ssl_engine_io.c
++++ b/modules/ssl/ssl_engine_io.c
+@@ -877,6 +877,8 @@ static apr_status_t ssl_filter_write(ap_filter_t *f,
+ */
+ outctx->c->cs->sense = CONN_SENSE_WANT_READ;
+ outctx->rc = APR_EAGAIN;
++ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, outctx->c,
++ "Want read during nonblocking write");
+ }
+ else if (ssl_err == SSL_ERROR_SYSCALL) {
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, outctx->rc, c, APLOGNO(01993)
+@@ -2071,6 +2073,8 @@ void ssl_io_filter_init(conn_rec *c, request_rec *r, SSL *ssl)
+ /* write is non blocking for the benefit of async mpm */
+ if (c->cs) {
+ BIO_set_nbio(filter_ctx->pbioWrite, 1);
++ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, c,
++ "Enabling non-blocking writes");
+ }
+
+ ssl_io_input_add_filter(filter_ctx, c, r, ssl);
+@@ -2114,9 +2118,8 @@ void ssl_io_filter_register(apr_pool_t *p)
+
+ #define DUMP_WIDTH 16
+
+-static void ssl_io_data_dump(server_rec *s,
+- const char *b,
+- long len)
++static void ssl_io_data_dump(conn_rec *c, server_rec *s,
++ const char *b, long len)
+ {
+ char buf[256];
+ char tmp[64];
+@@ -2129,7 +2132,7 @@ static void ssl_io_data_dump(server_rec *s,
+ rows = (len / DUMP_WIDTH);
+ if ((rows * DUMP_WIDTH) < len)
+ rows++;
+- ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, s,
++ ap_log_cserror(APLOG_MARK, APLOG_TRACE7, 0, c, s,
+ "+-------------------------------------------------------------------------+");
+ for(i = 0 ; i< rows; i++) {
+ #if APR_CHARSET_EBCDIC
+@@ -2168,12 +2171,12 @@ static void ssl_io_data_dump(server_rec *s,
+ }
+ }
+ apr_cpystrn(buf+strlen(buf), " |", sizeof(buf)-strlen(buf));
+- ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, s, "%s", buf);
++ ap_log_cserror(APLOG_MARK, APLOG_TRACE7, 0, c, s, "%s", buf);
+ }
+ if (trunc > 0)
+- ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, s,
++ ap_log_cserror(APLOG_MARK, APLOG_TRACE7, 0, c, s,
+ "| %04ld - ", len + trunc);
+- ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, s,
++ ap_log_cserror(APLOG_MARK, APLOG_TRACE7, 0, c, s,
+ "+-------------------------------------------------------------------------+");
+ return;
+ }
+@@ -2195,15 +2198,21 @@ long ssl_io_data_cb(BIO *bio, int cmd,
+ if ( cmd == (BIO_CB_WRITE|BIO_CB_RETURN)
+ || cmd == (BIO_CB_READ |BIO_CB_RETURN) ) {
+ if (rc >= 0) {
++ const char *dump = "";
++ if (APLOG_CS_IS_LEVEL(c, s, APLOG_TRACE7)) {
++ if (argp != NULL)
++ dump = "(BIO dump follows)";
++ else
++ dump = "(Oops, no memory buffer?)";
++ }
+ ap_log_cserror(APLOG_MARK, APLOG_TRACE4, 0, c, s,
+ "%s: %s %ld/%d bytes %s BIO#%pp [mem: %pp] %s",
+ MODSSL_LIBRARY_NAME,
+ (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"),
+ rc, argi, (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "to" : "from"),
+- bio, argp,
+- (argp != NULL ? "(BIO dump follows)" : "(Oops, no memory buffer?)"));
+- if ((argp != NULL) && APLOG_CS_IS_LEVEL(c, s, APLOG_TRACE7))
+- ssl_io_data_dump(s, argp, rc);
++ bio, argp, dump);
++ if (*dump != '\0' && argp != NULL)
++ ssl_io_data_dump(c, s, argp, rc);
+ }
+ else {
+ ap_log_cserror(APLOG_MARK, APLOG_TRACE4, 0, c, s,
+diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
+index e34fc55fa2..9a89ff9da8 100644
+--- a/modules/ssl/ssl_engine_kernel.c
++++ b/modules/ssl/ssl_engine_kernel.c
+@@ -839,10 +839,8 @@ int ssl_hook_Access(request_rec *r)
+ * request body, and then to reinject that request body later.
+ */
+ if (renegotiate && !renegotiate_quick
+- && (apr_table_get(r->headers_in, "transfer-encoding")
+- || (apr_table_get(r->headers_in, "content-length")
+- && strcmp(apr_table_get(r->headers_in, "content-length"), "0")))
+- && !r->expecting_100) {
++ && !r->expecting_100
++ && ap_request_has_body(r)) {
+ int rv;
+ apr_size_t rsize;
+
+@@ -2162,7 +2160,7 @@ static apr_status_t init_vhost(conn_rec *c, SSL *ssl)
+
+ if (SSL_check_private_key(ssl) < 1) {
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10088)
+- "Challenbge certificate and private key %s "
++ "Challenge certificate and private key %s "
+ "do not match", servername);
+ return APR_EGENERAL;
+ }
+@@ -2334,7 +2332,9 @@ int ssl_callback_SessionTicket(SSL *ssl,
+ }
+
+ memcpy(keyname, ticket_key->key_name, 16);
+- RAND_bytes(iv, EVP_MAX_IV_LENGTH);
++ if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) != 1) {
++ return -1;
++ }
+ EVP_EncryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), NULL,
+ ticket_key->aes_key, iv);
+ HMAC_Init_ex(hctx, ticket_key->hmac_secret, 16, tlsext_tick_md(), NULL);
+diff --git a/server/core_filters.c b/server/core_filters.c
+index ddc2ff7f0f..a6c2bd666b 100644
+--- a/server/core_filters.c
++++ b/server/core_filters.c
+@@ -378,6 +378,7 @@ apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *new_bb)
+ apr_size_t bytes_in_brigade, non_file_bytes_in_brigade;
+ int eor_buckets_in_brigade, morphing_bucket_in_brigade;
+ apr_status_t rv;
++ int loglevel = ap_get_conn_module_loglevel(c, APLOG_MODULE_INDEX);
+
+ /* Fail quickly if the connection has already been aborted. */
+ if (c->aborted) {
+@@ -513,7 +514,7 @@ apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *new_bb)
+ || eor_buckets_in_brigade > MAX_REQUESTS_IN_PIPELINE) {
+ /* this segment of the brigade MUST be sent before returning. */
+
+- if (APLOGctrace6(c)) {
++ if (loglevel >= APLOG_TRACE6) {
+ char *reason = APR_BUCKET_IS_FLUSH(bucket) ?
+ "FLUSH bucket" :
+ (non_file_bytes_in_brigade >= THRESHOLD_MAX_BUFFER) ?
+@@ -521,8 +522,17 @@ apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *new_bb)
+ morphing_bucket_in_brigade ? "morphing bucket" :
+ "MAX_REQUESTS_IN_PIPELINE";
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, c,
+- "core_output_filter: flushing because of %s",
+- reason);
++ "will flush because of %s", reason);
++ ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c,
++ "seen in brigade%s: bytes: %" APR_SIZE_T_FMT
++ ", non-file bytes: %" APR_SIZE_T_FMT ", eor "
++ "buckets: %d, morphing buckets: %d",
++ flush_upto == NULL ? " so far"
++ : " since last flush point",
++ bytes_in_brigade,
++ non_file_bytes_in_brigade,
++ eor_buckets_in_brigade,
++ morphing_bucket_in_brigade);
+ }
+ /*
+ * Defer the actual blocking write to avoid doing many writes.
+@@ -539,6 +549,10 @@ apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *new_bb)
+ if (flush_upto != NULL) {
+ ctx->tmp_flush_bb = apr_brigade_split_ex(bb, flush_upto,
+ ctx->tmp_flush_bb);
++ if (loglevel >= APLOG_TRACE8) {
++ ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c,
++ "flushing now");
++ }
+ rv = send_brigade_blocking(net->client_socket, bb,
+ &(ctx->bytes_written), c);
+ if (rv != APR_SUCCESS) {
+@@ -549,9 +563,23 @@ apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *new_bb)
+ c->aborted = 1;
+ return rv;
+ }
++ if (loglevel >= APLOG_TRACE8) {
++ ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c,
++ "total bytes written: %" APR_SIZE_T_FMT,
++ ctx->bytes_written);
++ }
+ APR_BRIGADE_CONCAT(bb, ctx->tmp_flush_bb);
+ }
+
++ if (loglevel >= APLOG_TRACE8) {
++ ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c,
++ "brigade contains: bytes: %" APR_SIZE_T_FMT
++ ", non-file bytes: %" APR_SIZE_T_FMT
++ ", eor buckets: %d, morphing buckets: %d",
++ bytes_in_brigade, non_file_bytes_in_brigade,
++ eor_buckets_in_brigade, morphing_bucket_in_brigade);
++ }
++
+ if (bytes_in_brigade >= THRESHOLD_MIN_WRITE) {
+ rv = send_brigade_nonblocking(net->client_socket, bb,
+ &(ctx->bytes_written), c);
+@@ -563,6 +591,12 @@ apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *new_bb)
+ c->aborted = 1;
+ return rv;
+ }
++ if (loglevel >= APLOG_TRACE8) {
++ ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c,
++ "tried nonblocking write, total bytes "
++ "written: %" APR_SIZE_T_FMT,
++ ctx->bytes_written);
++ }
+ }
+
+ setaside_remaining_output(f, ctx, bb, c);
+diff --git a/server/protocol.c b/server/protocol.c
+index 708160f30b..2ca6b124a8 100644
+--- a/server/protocol.c
++++ b/server/protocol.c
+@@ -894,7 +894,7 @@ rrl_done:
+ else if (deferred_error == rrl_baduri)
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03454)
+ "HTTP Request Line; URI incorrectly encoded: '%.*s'",
+- field_name_len(r->uri), r->uri);
++ field_name_len(r->unparsed_uri), r->unparsed_uri);
+ else if (deferred_error == rrl_badwhitespace)
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03447)
+ "HTTP Request Line; Invalid whitespace");
+diff --git a/server/util_script.c b/server/util_script.c
+index 4121ae0aec..599ba58e71 100644
+--- a/server/util_script.c
++++ b/server/util_script.c
+@@ -260,9 +260,8 @@ AP_DECLARE(void) ap_add_common_vars(request_rec *r)
+ apr_table_addn(e, "CONTEXT_DOCUMENT_ROOT", ap_context_document_root(r));
+ apr_table_addn(e, "SERVER_ADMIN", s->server_admin); /* Apache */
+ if (apr_table_get(r->notes, "proxy-noquery") && (q = ap_strchr(r->filename, '?'))) {
+- *q = '\0';
+- apr_table_addn(e, "SCRIPT_FILENAME", apr_pstrdup(r->pool, r->filename));
+- *q = '?';
++ char *script_filename = apr_pstrmemdup(r->pool, r->filename, q - r->filename);
++ apr_table_addn(e, "SCRIPT_FILENAME", script_filename);
+ }
+ else {
+ apr_table_addn(e, "SCRIPT_FILENAME", r->filename); /* Apache */
+--
+2.19.1
+
diff --git a/Merge-r1831773-from-trunk.patch b/Merge-r1831773-from-trunk.patch
new file mode 100644
index 0000000..2d4608c
--- /dev/null
+++ b/Merge-r1831773-from-trunk.patch
@@ -0,0 +1,87 @@
+From 5e6fa5d12a569b7b780139c30542a49c78993bce Mon Sep 17 00:00:00 2001
+From: Jim Jagielski
+Date: Tue, 11 Dec 2018 14:09:11 +0000
+Subject: [PATCH 293/504] Merge r1831773 from trunk:
+
+PR62311: only create the rewritelock when needed
+
+Submitted By: Hank Ibell
+Committed By: covener
+
+
+
+Submitted by: covener
+Reviewed by: jailletc36, icing (by inspection), covener
+
+
+git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1848681 13f79535-47bb-0310-9956-ffa450edef68
+---
+ CHANGES | 4 ++++
+ STATUS | 5 -----
+ modules/mappers/mod_rewrite.c | 21 ++++++++++++---------
+ 3 files changed, 16 insertions(+), 14 deletions(-)
+
+diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c
+index fb897a9760..68a33b68a5 100644
+--- a/modules/mappers/mod_rewrite.c
++++ b/modules/mappers/mod_rewrite.c
+@@ -416,6 +416,7 @@ static cache *cachep;
+ static int proxy_available;
+
+ /* Locks/Mutexes */
++static int rewrite_lock_needed = 0;
+ static apr_global_mutex_t *rewrite_mapr_lock_acquire = NULL;
+ static const char *rewritemap_mutex_type = "rewrite-map";
+
+@@ -2687,9 +2688,6 @@ static apr_status_t rewritelock_create(server_rec *s, apr_pool_t *p)
+ apr_status_t rc;
+
+ /* create the lockfile */
+- /* XXX See if there are any rewrite map programs before creating
+- * the mutex.
+- */
+ rc = ap_global_mutex_create(&rewrite_mapr_lock_acquire, NULL,
+ rewritemap_mutex_type, NULL, s, p, 0);
+ if (rc != APR_SUCCESS) {
+@@ -3163,6 +3161,8 @@ static const char *cmd_rewritemap(cmd_parms *cmd, void *dconf, const char *a1,
+
+ newmap->type = MAPTYPE_PRG;
+ newmap->checkfile = newmap->argv[0];
++ rewrite_lock_needed = 1;
++
+ if (a3) {
+ char *tok_cntx;
+ newmap->user = apr_strtok(apr_pstrdup(cmd->pool, a3), ":", &tok_cntx);
+@@ -4469,6 +4469,7 @@ static int pre_config(apr_pool_t *pconf,
+ {
+ APR_OPTIONAL_FN_TYPE(ap_register_rewrite_mapfunc) *map_pfn_register;
+
++ rewrite_lock_needed = 0;
+ ap_mutex_register(pconf, rewritemap_mutex_type, NULL, APR_LOCK_DEFAULT, 0);
+
+ /* register int: rewritemap handlers */
+@@ -4494,13 +4495,15 @@ static int post_config(apr_pool_t *p,
+ /* check if proxy module is available */
+ proxy_available = (ap_find_linked_module("mod_proxy.c") != NULL);
+
+- rv = rewritelock_create(s, p);
+- if (rv != APR_SUCCESS) {
+- return HTTP_INTERNAL_SERVER_ERROR;
+- }
++ if (rewrite_lock_needed) {
++ rv = rewritelock_create(s, p);
++ if (rv != APR_SUCCESS) {
++ return HTTP_INTERNAL_SERVER_ERROR;
++ }
+
+- apr_pool_cleanup_register(p, (void *)s, rewritelock_remove,
+- apr_pool_cleanup_null);
++ apr_pool_cleanup_register(p, (void *)s, rewritelock_remove,
++ apr_pool_cleanup_null);
++ }
+
+ /* if we are not doing the initial config, step through the servers and
+ * open the RewriteMap prg:xxx programs,
+--
+2.19.1
+
diff --git a/Merge-r1837130-from-trunk.patch b/Merge-r1837130-from-trunk.patch
new file mode 100644
index 0000000..aa9bf0b
--- /dev/null
+++ b/Merge-r1837130-from-trunk.patch
@@ -0,0 +1,108 @@
+From ae583b572b6ce34e5fdeb92c88fde44d18db2db7 Mon Sep 17 00:00:00 2001
+From: Yann Ylavic
+Date: Tue, 28 Aug 2018 20:07:07 +0000
+Subject: [PATCH 102/504] Merge r1837130 from trunk:
+
+mod_ratelimit: Don't interfere with "chunked" encoding.
+
+By the time ap_http_header_filter() sends the header brigade and adds the
+"CHUNK" filter, we need to garantee that the header went through all the
+filters' stack, and more specifically above ap_http_chunk_filter() which
+assumes that all it receives is content data.
+Since rate_limit_filter() may retain the header brigade, make it run after
+ap_http_chunk_filter(), just before AP_FTYPE_CONNECTION filters.
+
+Also, ap_http_header_filter() shouldn't eat the EOS for HEAD/no-body responses.
+For instance mod_ratelimit depends on it since r1835168, but any next request
+filter may as well to flush and/or bail out approprietely.
+
+This fixes the regression introduced in 2.4.34 (r1835168).
+PR 62568.
+
+Submitted by: ylavic
+Reviewed by: covener, ylavic, jim
+
+
+git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1839497 13f79535-47bb-0310-9956-ffa450edef68
+---
+ CHANGES | 3 +++
+ STATUS | 7 -------
+ modules/filters/mod_ratelimit.c | 3 +--
+ modules/http/chunk_filter.c | 3 ++-
+ modules/http/http_filters.c | 13 ++++++++++++-
+ 5 files changed, 18 insertions(+), 11 deletions(-)
+
+diff --git a/modules/filters/mod_ratelimit.c b/modules/filters/mod_ratelimit.c
+index cf79973120..d16eb39059 100644
+--- a/modules/filters/mod_ratelimit.c
++++ b/modules/filters/mod_ratelimit.c
+@@ -65,7 +65,6 @@ rate_limit_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+
+ /* Set up our rl_ctx_t on first use */
+ if (ctx == NULL) {
+-
+ const char *rl = NULL;
+ int ratelimit;
+ int burst = 0;
+@@ -327,7 +326,7 @@ static void register_hooks(apr_pool_t *p)
+ {
+ /* run after mod_deflate etc etc, but not at connection level, ie, mod_ssl. */
+ ap_register_output_filter(RATE_LIMIT_FILTER_NAME, rate_limit_filter,
+- NULL, AP_FTYPE_PROTOCOL + 3);
++ NULL, AP_FTYPE_CONNECTION - 1);
+ }
+
+ AP_DECLARE_MODULE(ratelimit) = {
+diff --git a/modules/http/chunk_filter.c b/modules/http/chunk_filter.c
+index 17fbabdb0a..cb1501aebf 100644
+--- a/modules/http/chunk_filter.c
++++ b/modules/http/chunk_filter.c
+@@ -69,6 +69,7 @@ apr_status_t ap_http_chunk_filter(ap_filter_t *f, apr_bucket_brigade *b)
+ {
+ if (APR_BUCKET_IS_EOS(e)) {
+ /* there shouldn't be anything after the eos */
++ ap_remove_output_filter(f);
+ eos = e;
+ break;
+ }
+@@ -186,11 +187,11 @@ apr_status_t ap_http_chunk_filter(ap_filter_t *f, apr_bucket_brigade *b)
+
+ /* pass the brigade to the next filter. */
+ rv = ap_pass_brigade(f->next, b);
++ apr_brigade_cleanup(b);
+ if (rv != APR_SUCCESS || eos != NULL) {
+ return rv;
+ }
+ tmp = b;
+- apr_brigade_cleanup(tmp);
+ }
+ return APR_SUCCESS;
+ }
+diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c
+index 5fa40635d8..37c0113e5b 100644
+--- a/modules/http/http_filters.c
++++ b/modules/http/http_filters.c
+@@ -1308,8 +1308,19 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
+ else if (ctx->headers_sent) {
+ /* Eat body if response must not have one. */
+ if (r->header_only || r->status == HTTP_NO_CONTENT) {
++ /* Still next filters may be waiting for EOS, so pass it (alone)
++ * when encountered and be done with this filter.
++ */
++ e = APR_BRIGADE_LAST(b);
++ if (e != APR_BRIGADE_SENTINEL(b) && APR_BUCKET_IS_EOS(e)) {
++ APR_BUCKET_REMOVE(e);
++ apr_brigade_cleanup(b);
++ APR_BRIGADE_INSERT_HEAD(b, e);
++ ap_remove_output_filter(f);
++ rv = ap_pass_brigade(f->next, b);
++ }
+ apr_brigade_cleanup(b);
+- return APR_SUCCESS;
++ return rv;
+ }
+ }
+
+--
+2.19.1
+
diff --git a/Merge-r1837250-from-trunk.patch b/Merge-r1837250-from-trunk.patch
new file mode 100644
index 0000000..9ef0da0
--- /dev/null
+++ b/Merge-r1837250-from-trunk.patch
@@ -0,0 +1,55 @@
+From 657d20fe0dbe55a793ca48fb6f58ee497d241934 Mon Sep 17 00:00:00 2001
+From: Jim Jagielski
+Date: Wed, 7 Nov 2018 15:18:42 +0000
+Subject: [PATCH 261/504] Merge r1837250 from trunk:
+
+If ProxyPassReverse is used for reverse mapping of relative redirects, subsequent ProxyPassReverse statements, whether they are relative or absolute, may fail.
+
+PR 60408 [Peter Haworth ]
+Submitted by: jailletc36
+Reviewed by: jailletc36, rpluem, jim
+
+
+git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1846044 13f79535-47bb-0310-9956-ffa450edef68
+---
+ CHANGES | 4 ++++
+ STATUS | 6 ------
+ modules/proxy/proxy_util.c | 8 ++++++--
+ 3 files changed, 10 insertions(+), 8 deletions(-)
+
+diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
+index 6501c68064..cbf8826777 100644
+--- a/modules/proxy/proxy_util.c
++++ b/modules/proxy/proxy_util.c
+@@ -837,7 +837,7 @@ PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r,
+ {
+ proxy_req_conf *rconf;
+ struct proxy_alias *ent;
+- int i, l1, l2;
++ int i, l1, l1_orig, l2;
+ char *u;
+
+ /*
+@@ -849,7 +849,7 @@ PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r,
+ return url;
+ }
+
+- l1 = strlen(url);
++ l1_orig = strlen(url);
+ if (conf->interpolate_env == 1) {
+ rconf = ap_get_module_config(r->request_config, &proxy_module);
+ ent = (struct proxy_alias *)rconf->raliases->elts;
+@@ -862,6 +862,10 @@ PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r,
+ ap_get_module_config(r->server->module_config, &proxy_module);
+ proxy_balancer *balancer;
+ const char *real = ent[i].real;
++
++ /* Restore the url length, if it had been changed by the code below */
++ l1 = l1_orig;
++
+ /*
+ * First check if mapping against a balancer and see
+ * if we have such a entity. If so, then we need to
+--
+2.19.1
+
diff --git a/Merge-r1842540-from-trunk.patch b/Merge-r1842540-from-trunk.patch
new file mode 100644
index 0000000..b66d9f5
--- /dev/null
+++ b/Merge-r1842540-from-trunk.patch
@@ -0,0 +1,75 @@
+From 5c1995151ab80cd71bc845bd288b90fd55665e2e Mon Sep 17 00:00:00 2001
+From: Eric Covener
+Date: Tue, 9 Oct 2018 23:26:35 +0000
+Subject: [PATCH 209/504] Merge r1842540 from trunk:
+
+* Pickup the proxy related configuration for verify mode and verify depth and
+ not the configuration settings for frontend connections in case of
+ connections by the proxy to the backend.
+
+PR: 62769
+
+
+
+git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1843370 13f79535-47bb-0310-9956-ffa450edef68
+---
+ CHANGES | 4 ++++
+ STATUS | 9 ---------
+ modules/ssl/ssl_engine_kernel.c | 25 ++++++++++++++++++-------
+ 3 files changed, 22 insertions(+), 16 deletions(-)
+
+diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
+index d576a298ec..6cd0da527f 100644
+--- a/modules/ssl/ssl_engine_kernel.c
++++ b/modules/ssl/ssl_engine_kernel.c
+@@ -1740,7 +1740,8 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
+ /* Get verify ingredients */
+ int errnum = X509_STORE_CTX_get_error(ctx);
+ int errdepth = X509_STORE_CTX_get_error_depth(ctx);
+- int depth, verify;
++ int depth = UNSET;
++ int verify = SSL_CVERIFY_UNSET;
+
+ /*
+ * Log verification information
+@@ -1756,10 +1757,15 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
+ /*
+ * Check for optionally acceptable non-verifiable issuer situation
+ */
+- if (dc && (dc->nVerifyClient != SSL_CVERIFY_UNSET)) {
+- verify = dc->nVerifyClient;
++ if (dc) {
++ if (sslconn->is_proxy) {
++ verify = dc->proxy->auth.verify_mode;
++ }
++ else {
++ verify = dc->nVerifyClient;
++ }
+ }
+- else {
++ if (!dc || (verify == SSL_CVERIFY_UNSET)) {
+ verify = mctx->auth.verify_mode;
+ }
+
+@@ -1863,10 +1869,15 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
+ /*
+ * Finally check the depth of the certificate verification
+ */
+- if (dc && (dc->nVerifyDepth != UNSET)) {
+- depth = dc->nVerifyDepth;
++ if (dc) {
++ if (sslconn->is_proxy) {
++ depth = dc->proxy->auth.verify_depth;
++ }
++ else {
++ depth = dc->nVerifyDepth;
++ }
+ }
+- else {
++ if (!dc || (depth == UNSET)) {
+ depth = mctx->auth.verify_depth;
+ }
+
+--
+2.19.1
+
diff --git a/Merge-r1851093-from-trunk1.patch b/Merge-r1851093-from-trunk1.patch
new file mode 100644
index 0000000..701603a
--- /dev/null
+++ b/Merge-r1851093-from-trunk1.patch
@@ -0,0 +1,33 @@
+From b34ce8ef855714a8a90e4e97cd7f2b8f8027c7fe Mon Sep 17 00:00:00 2001
+From: Christophe Jaillet
+Date: Fri, 15 Feb 2019 15:57:51 +0000
+Subject: [PATCH 390/504] Merge r1851093 from trunk
+
+ * mod_proxy_wstunnel: Fix websocket proxy over UDS.
+
+PR: 62932
+Submitted by:
+Reviewed by: jailletc36 (by inspection), jim, ylavic
+Backported by: jailletc36
+
+git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1853653 13f79535-47bb-0310-9956-ffa450edef68
+---
+diff --git a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunnel.c
+index 9dda010dbc..1056d5cb72 100644
+--- a/modules/proxy/mod_proxy_wstunnel.c
++++ b/modules/proxy/mod_proxy_wstunnel.c
+@@ -77,7 +77,10 @@ static int proxy_wstunnel_canon(request_rec *r, char *url)
+ if (path == NULL)
+ return HTTP_BAD_REQUEST;
+
+- apr_snprintf(sport, sizeof(sport), ":%d", port);
++ if (port != def_port)
++ apr_snprintf(sport, sizeof(sport), ":%d", port);
++ else
++ sport[0] = '\0';
+
+ if (ap_strchr_c(host, ':')) {
+ /* if literal IPv6 address */
+--
+2.19.1
+
diff --git a/Merge-r1851093-from-trunk2.patch b/Merge-r1851093-from-trunk2.patch
new file mode 100644
index 0000000..c3b18a7
--- /dev/null
+++ b/Merge-r1851093-from-trunk2.patch
@@ -0,0 +1,37 @@
+From 7e6e9c0a7b9653af14ddce21c9cebf9765c70823 Mon Sep 17 00:00:00 2001
+From: Christophe Jaillet
+Date: Fri, 15 Feb 2019 16:06:24 +0000
+Subject: [PATCH 391/504] Merge r1851093 from trunk
+
+ * mod_proxy_wstunnel: Fix websocket proxy over UDS.
+
+PR: 62932
+Submitted by:
+Reviewed by: jailletc36 (by inspection), jim, ylavic
+Backported by: jailletc36
+
+git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1853654 13f79535-47bb-0310-9956-ffa450edef68
+---
+ modules/aaa/mod_authn_dbm.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/modules/aaa/mod_authn_dbm.c b/modules/aaa/mod_authn_dbm.c
+index f4fb73672e..6eae9e052a 100644
+--- a/modules/aaa/mod_authn_dbm.c
++++ b/modules/aaa/mod_authn_dbm.c
+@@ -102,7 +102,11 @@ static apr_status_t fetch_dbm_value(const char *dbmtype, const char *dbmfile,
+
+ apr_dbm_close(f);
+
+- return rv;
++ /* NOT FOUND is not an error case; this is indicated by a NULL result.
++ * Treat all NULL lookup/error results as success for the simple case
++ * of auth credential lookup, these are DECLINED in both cases.
++ */
++ return APR_SUCCESS;
+ }
+
+ static authn_status check_dbm_pw(request_rec *r, const char *user,
+--
+2.19.1
+
diff --git a/Merge-r1853190-from-trunk.patch b/Merge-r1853190-from-trunk.patch
new file mode 100644
index 0000000..086073e
--- /dev/null
+++ b/Merge-r1853190-from-trunk.patch
@@ -0,0 +1,135 @@
+From 44b3ddc560c490c60600998fa2bf59b142d08e05 Mon Sep 17 00:00:00 2001
+From: Joe Orton
+Date: Tue, 12 Mar 2019 09:24:26 +0000
+Subject: [PATCH 408/504] Merge r1853190 from trunk:
+
+Fix a race condition. Authentication with valid credentials could be
+refused in case of concurrent accesses from different users.
+
+PR: 63124
+Submitted by: Simon Kappel
+Reviewed by: jailletc36, icing, jorton
+
+
+git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1855298 13f79535-47bb-0310-9956-ffa450edef68
+---
+ CHANGES | 4 ++++
+ modules/aaa/mod_auth_digest.c | 26 ++++++++++++--------------
+ 2 files changed, 16 insertions(+), 14 deletions(-)
+
+diff --git a/modules/aaa/mod_auth_digest.c b/modules/aaa/mod_auth_digest.c
+index a67f06986f..b76094114d 100644
+--- a/modules/aaa/mod_auth_digest.c
++++ b/modules/aaa/mod_auth_digest.c
+@@ -92,7 +92,6 @@ typedef struct digest_config_struct {
+ int check_nc;
+ const char *algorithm;
+ char *uri_list;
+- const char *ha1;
+ } digest_config_rec;
+
+
+@@ -153,6 +152,7 @@ typedef struct digest_header_struct {
+ apr_time_t nonce_time;
+ enum hdr_sts auth_hdr_sts;
+ int needed_auth;
++ const char *ha1;
+ client_entry *client;
+ } digest_header_rec;
+
+@@ -1304,7 +1304,7 @@ static int hook_note_digest_auth_failure(request_rec *r, const char *auth_type)
+ */
+
+ static authn_status get_hash(request_rec *r, const char *user,
+- digest_config_rec *conf)
++ digest_config_rec *conf, const char **rethash)
+ {
+ authn_status auth_result;
+ char *password;
+@@ -1356,7 +1356,7 @@ static authn_status get_hash(request_rec *r, const char *user,
+ } while (current_provider);
+
+ if (auth_result == AUTH_USER_FOUND) {
+- conf->ha1 = password;
++ *rethash = password;
+ }
+
+ return auth_result;
+@@ -1483,25 +1483,24 @@ static int check_nonce(request_rec *r, digest_header_rec *resp,
+
+ /* RFC-2069 */
+ static const char *old_digest(const request_rec *r,
+- const digest_header_rec *resp, const char *ha1)
++ const digest_header_rec *resp)
+ {
+ const char *ha2;
+
+ ha2 = ap_md5(r->pool, (unsigned char *)apr_pstrcat(r->pool, resp->method, ":",
+ resp->uri, NULL));
+ return ap_md5(r->pool,
+- (unsigned char *)apr_pstrcat(r->pool, ha1, ":", resp->nonce,
+- ":", ha2, NULL));
++ (unsigned char *)apr_pstrcat(r->pool, resp->ha1, ":",
++ resp->nonce, ":", ha2, NULL));
+ }
+
+ /* RFC-2617 */
+ static const char *new_digest(const request_rec *r,
+- digest_header_rec *resp,
+- const digest_config_rec *conf)
++ digest_header_rec *resp)
+ {
+ const char *ha1, *ha2, *a2;
+
+- ha1 = conf->ha1;
++ ha1 = resp->ha1;
+
+ a2 = apr_pstrcat(r->pool, resp->method, ":", resp->uri, NULL);
+ ha2 = ap_md5(r->pool, (const unsigned char *)a2);
+@@ -1514,7 +1513,6 @@ static const char *new_digest(const request_rec *r,
+ NULL));
+ }
+
+-
+ static void copy_uri_components(apr_uri_t *dst,
+ apr_uri_t *src, request_rec *r) {
+ if (src->scheme && src->scheme[0] != '\0') {
+@@ -1759,7 +1757,7 @@ static int authenticate_digest_user(request_rec *r)
+ return HTTP_UNAUTHORIZED;
+ }
+
+- return_code = get_hash(r, r->user, conf);
++ return_code = get_hash(r, r->user, conf, &resp->ha1);
+
+ if (return_code == AUTH_USER_NOT_FOUND) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01790)
+@@ -1789,7 +1787,7 @@ static int authenticate_digest_user(request_rec *r)
+
+ if (resp->message_qop == NULL) {
+ /* old (rfc-2069) style digest */
+- if (strcmp(resp->digest, old_digest(r, resp, conf->ha1))) {
++ if (strcmp(resp->digest, old_digest(r, resp))) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01792)
+ "user %s: password mismatch: %s", r->user,
+ r->uri);
+@@ -1819,7 +1817,7 @@ static int authenticate_digest_user(request_rec *r)
+ return HTTP_UNAUTHORIZED;
+ }
+
+- exp_digest = new_digest(r, resp, conf);
++ exp_digest = new_digest(r, resp);
+ if (!exp_digest) {
+ /* we failed to allocate a client struct */
+ return HTTP_INTERNAL_SERVER_ERROR;
+@@ -1903,7 +1901,7 @@ static int add_auth_info(request_rec *r)
+
+ /* calculate rspauth attribute
+ */
+- ha1 = conf->ha1;
++ ha1 = resp->ha1;
+
+ a2 = apr_pstrcat(r->pool, ":", resp->uri, NULL);
+ ha2 = ap_md5(r->pool, (const unsigned char *)a2);
+--
+2.19.1
+
diff --git a/Merge-r1855646-r1855748-from-trunk.patch b/Merge-r1855646-r1855748-from-trunk.patch
new file mode 100644
index 0000000..554b1d9
--- /dev/null
+++ b/Merge-r1855646-r1855748-from-trunk.patch
@@ -0,0 +1,143 @@
+From 0dcd178c561f3293775a3d5953c764d64a5af233 Mon Sep 17 00:00:00 2001
+From: Joe Orton
+Date: Wed, 20 Mar 2019 15:50:44 +0000
+Subject: [PATCH 475/504] Merge r1855646, r1855748 from trunk:
+
+mod_proxy/ssl: cleanup per-request SSL configuration for recycled proxy conns.
+
+The SSL dir config of proxy/backend connections is stored in r->per_dir_config
+but those connections have a lifetime independent of the requests they handle.
+
+So we need to allow the external ssl_engine_set() function to reset mod_ssl's
+dir config in between proxy requests, or the first sslconn->dc could be used
+after free for the next requests.
+
+mod_proxy can then reset/reinit the request config when recycling its backend
+connections.
+
+* Solve a chicken and egg problem here:
+ We need to have sslconn->dc set correctly when we want to
+ init sslconn, but we need to allocate memory for it first.
+
+PR 63256.
+Submitted by: ylavic, rpluem
+Reviewed by: ylavic, jorton, jim
+
+
+git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1855918 13f79535-47bb-0310-9956-ffa450edef68
+---
+ CHANGES | 4 ++++
+ modules/proxy/proxy_util.c | 13 +++++++++++++
+ modules/ssl/mod_ssl.c | 38 ++++++++++++++++++++++++--------------
+ 3 files changed, 41 insertions(+), 14 deletions(-)
+
+diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
+index cbf8826777..b131ec07f6 100644
+--- a/modules/proxy/proxy_util.c
++++ b/modules/proxy/proxy_util.c
+@@ -1532,6 +1532,13 @@ static apr_status_t connection_cleanup(void *theconn)
+ socket_cleanup(conn);
+ conn->close = 0;
+ }
++ else if (conn->is_ssl) {
++ /* Unbind/reset the SSL connection dir config (sslconn->dc) from
++ * r->per_dir_config, r will likely get destroyed before this proxy
++ * conn is reused.
++ */
++ ap_proxy_ssl_engine(conn->connection, worker->section_config, 1);
++ }
+
+ if (worker->s->hmax && worker->cp->res) {
+ conn->inreslist = 1;
+@@ -3172,6 +3179,12 @@ static int proxy_connection_create(const char *proxy_function,
+ apr_bucket_alloc_t *bucket_alloc;
+
+ if (conn->connection) {
++ if (conn->is_ssl) {
++ /* on reuse, reinit the SSL connection dir config with the current
++ * r->per_dir_config, the previous one was reset on release.
++ */
++ ap_proxy_ssl_engine(conn->connection, per_dir_config, 1);
++ }
+ return OK;
+ }
+
+diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c
+index 4797c78bb9..e857f50647 100644
+--- a/modules/ssl/mod_ssl.c
++++ b/modules/ssl/mod_ssl.c
+@@ -442,17 +442,20 @@ static int ssl_hook_pre_config(apr_pool_t *pconf,
+ }
+
+ static SSLConnRec *ssl_init_connection_ctx(conn_rec *c,
+- ap_conf_vector_t *per_dir_config)
++ ap_conf_vector_t *per_dir_config,
++ int new_proxy)
+ {
+ SSLConnRec *sslconn = myConnConfig(c);
+- SSLSrvConfigRec *sc;
++ int need_setup = 0;
+
+- if (sslconn) {
+- return sslconn;
++ if (!sslconn) {
++ sslconn = apr_pcalloc(c->pool, sizeof(*sslconn));
++ need_setup = 1;
+ }
+
+- sslconn = apr_pcalloc(c->pool, sizeof(*sslconn));
+-
++ /* Reinit dc in any case because it may be r->per_dir_config scoped
++ * and thus a caller like mod_proxy needs to update it per request.
++ */
+ if (per_dir_config) {
+ sslconn->dc = ap_get_module_config(per_dir_config, &ssl_module);
+ }
+@@ -461,12 +464,20 @@ static SSLConnRec *ssl_init_connection_ctx(conn_rec *c,
+ &ssl_module);
+ }
+
+- sslconn->server = c->base_server;
+- sslconn->verify_depth = UNSET;
+- sc = mySrvConfig(c->base_server);
+- sslconn->cipher_suite = sc->server->auth.cipher_suite;
++ if (need_setup) {
++ sslconn->server = c->base_server;
++ sslconn->verify_depth = UNSET;
++ if (new_proxy) {
++ sslconn->is_proxy = 1;
++ sslconn->cipher_suite = sslconn->dc->proxy->auth.cipher_suite;
++ }
++ else {
++ SSLSrvConfigRec *sc = mySrvConfig(c->base_server);
++ sslconn->cipher_suite = sc->server->auth.cipher_suite;
++ }
+
+- myConnConfigSet(c, sslconn);
++ myConnConfigSet(c, sslconn);
++ }
+
+ return sslconn;
+ }
+@@ -507,8 +518,7 @@ static int ssl_engine_set(conn_rec *c,
+ int status;
+
+ if (proxy) {
+- sslconn = ssl_init_connection_ctx(c, per_dir_config);
+- sslconn->is_proxy = 1;
++ sslconn = ssl_init_connection_ctx(c, per_dir_config, 1);
+ }
+ else {
+ sslconn = myConnConfig(c);
+@@ -555,7 +565,7 @@ int ssl_init_ssl_connection(conn_rec *c, request_rec *r)
+ /*
+ * Create or retrieve SSL context
+ */
+- sslconn = ssl_init_connection_ctx(c, r ? r->per_dir_config : NULL);
++ sslconn = ssl_init_connection_ctx(c, r ? r->per_dir_config : NULL, 0);
+ server = sslconn->server;
+ sc = mySrvConfig(server);
+
+--
+2.19.1
+
diff --git a/On-the-2.4.x-branch.patch b/On-the-2.4.x-branch.patch
new file mode 100644
index 0000000..53064c3
--- /dev/null
+++ b/On-the-2.4.x-branch.patch
@@ -0,0 +1,178 @@
+From c0457a9d97bc7b50cbc54f587fcca419d1a2ca2e Mon Sep 17 00:00:00 2001
+From: Stefan Eissing
+Date: Fri, 3 Aug 2018 10:54:47 +0000
+Subject: [PATCH 056/504] On the 2.4.x branch:
+
+backport of r1837357 from trunk.
+ *) mod_md: When the last domain name from an MD is moved to another one,
+ that now empty MD gets moved to the store archive. PR 62572.
+
+
+
+git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1837358 13f79535-47bb-0310-9956-ffa450edef68
+---
+ CHANGES | 3 +++
+ modules/md/md_reg.c | 60 +++++++++++++++++++++++------------------
+ modules/md/md_reg.h | 2 ++
+ modules/md/md_version.h | 4 +--
+ 4 files changed, 41 insertions(+), 28 deletions(-)
+
+diff --git a/modules/md/md_reg.c b/modules/md/md_reg.c
+index 459c15f157..233fea79d7 100644
+--- a/modules/md/md_reg.c
++++ b/modules/md/md_reg.c
+@@ -635,11 +635,10 @@ apr_status_t md_reg_creds_get(const md_creds_t **pcreds, md_reg_t *reg,
+
+ typedef struct {
+ apr_pool_t *p;
+- apr_array_header_t *conf_mds;
+ apr_array_header_t *store_mds;
+ } sync_ctx;
+
+-static int find_changes(void *baton, md_store_t *store, md_t *md, apr_pool_t *ptemp)
++static int do_add_md(void *baton, md_store_t *store, md_t *md, apr_pool_t *ptemp)
+ {
+ sync_ctx *ctx = baton;
+
+@@ -649,6 +648,18 @@ static int find_changes(void *baton, md_store_t *store, md_t *md, apr_pool_t *pt
+ return 1;
+ }
+
++static apr_status_t read_store_mds(md_reg_t *reg, sync_ctx *ctx)
++{
++ int rv;
++
++ apr_array_clear(ctx->store_mds);
++ rv = md_store_md_iter(do_add_md, ctx, reg->store, ctx->p, MD_SG_DOMAINS, "*");
++ if (APR_STATUS_IS_ENOENT(rv)) {
++ rv = APR_SUCCESS;
++ }
++ return rv;
++}
++
+ apr_status_t md_reg_set_props(md_reg_t *reg, apr_pool_t *p, int can_http, int can_https)
+ {
+ if (reg->can_http != can_http || reg->can_https != can_https) {
+@@ -686,17 +697,11 @@ apr_status_t md_reg_sync(md_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp,
+ apr_array_header_t *master_mds)
+ {
+ sync_ctx ctx;
+- md_store_t *store = reg->store;
+ apr_status_t rv;
+
+ ctx.p = ptemp;
+- ctx.conf_mds = master_mds;
+- ctx.store_mds = apr_array_make(ptemp, 100, sizeof(md_t *));
+-
+- rv = md_store_md_iter(find_changes, &ctx, store, ptemp, MD_SG_DOMAINS, "*");
+- if (APR_STATUS_IS_ENOENT(rv)) {
+- rv = APR_SUCCESS;
+- }
++ ctx.store_mds = apr_array_make(ptemp,100, sizeof(md_t *));
++ rv = read_store_mds(reg, &ctx);
+
+ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p,
+ "sync: found %d mds in store", ctx.store_mds->nelts);
+@@ -705,8 +710,8 @@ apr_status_t md_reg_sync(md_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp,
+ md_t *md, *config_md, *smd, *omd;
+ const char *common;
+
+- for (i = 0; i < ctx.conf_mds->nelts; ++i) {
+- md = APR_ARRAY_IDX(ctx.conf_mds, i, md_t *);
++ for (i = 0; i < master_mds->nelts; ++i) {
++ md = APR_ARRAY_IDX(master_mds, i, md_t *);
+
+ /* find the store md that is closest match for the configured md */
+ smd = md_find_closest_match(ctx.store_mds, md);
+@@ -734,7 +739,7 @@ apr_status_t md_reg_sync(md_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp,
+ assert(common);
+
+ /* Is this md still configured or has it been abandoned in the config? */
+- config_md = md_get_by_name(ctx.conf_mds, omd->name);
++ config_md = md_get_by_name(master_mds, omd->name);
+ if (config_md && md_contains(config_md, common, 0)) {
+ /* domain used in two configured mds, not allowed */
+ rv = APR_EINVAL;
+@@ -742,21 +747,19 @@ apr_status_t md_reg_sync(md_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp,
+ "domain %s used in md %s and %s",
+ common, md->name, omd->name);
+ }
+- else if (config_md) {
+- /* domain stored in omd, but no longer has the offending domain,
+- remove it from the store md. */
+- omd->domains = md_array_str_remove(ptemp, omd->domains, common, 0);
+- rv = md_reg_update(reg, ptemp, omd->name, omd, MD_UPD_DOMAINS);
+- }
+ else {
+- /* domain in a store md that is no longer configured, warn about it.
+- * Remove the domain here, so we can progress, but never save it. */
++ /* remove it from the other md and update store, or, if it
++ * is now empty, move it into the archive */
+ omd->domains = md_array_str_remove(ptemp, omd->domains, common, 0);
+- md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p,
+- "domain %s, configured in md %s, is part of the stored md %s."
+- " That md however is no longer mentioned in the config. "
+- "If you longer want it, remove the md from the store.",
+- common, md->name, omd->name);
++ if (apr_is_empty_array(omd->domains)) {
++ md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p,
++ "All domains of the MD %s have moved elsewhere, "
++ " moving it to the archive. ", omd->name);
++ md_reg_remove(reg, ptemp, omd->name, 1); /* best effort */
++ }
++ else {
++ rv = md_reg_update(reg, ptemp, omd->name, omd, MD_UPD_DOMAINS);
++ }
+ }
+ }
+
+@@ -841,6 +844,11 @@ apr_status_t md_reg_sync(md_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp,
+ return rv;
+ }
+
++apr_status_t md_reg_remove(md_reg_t *reg, apr_pool_t *p, const char *name, int archive)
++{
++ return md_store_move(reg->store, p, MD_SG_DOMAINS, MD_SG_ARCHIVE, name, archive);
++}
++
+
+ /**************************************************************************************************/
+ /* driving */
+diff --git a/modules/md/md_reg.h b/modules/md/md_reg.h
+index 2bf738583c..d976b7fe80 100644
+--- a/modules/md/md_reg.h
++++ b/modules/md/md_reg.h
+@@ -124,6 +124,8 @@ apr_status_t md_reg_get_cred_files(md_reg_t *reg, const md_t *md, apr_pool_t *p,
+ apr_status_t md_reg_sync(md_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp,
+ apr_array_header_t *master_mds);
+
++apr_status_t md_reg_remove(md_reg_t *reg, apr_pool_t *p, const char *name, int archive);
++
+ /**************************************************************************************************/
+ /* protocol drivers */
+
+diff --git a/modules/md/md_version.h b/modules/md/md_version.h
+index b87f19c1db..34ab4eb61e 100644
+--- a/modules/md/md_version.h
++++ b/modules/md/md_version.h
+@@ -27,7 +27,7 @@
+ * @macro
+ * Version number of the md module as c string
+ */
+-#define MOD_MD_VERSION "1.1.15"
++#define MOD_MD_VERSION "1.1.16"
+
+ /**
+ * @macro
+@@ -35,7 +35,7 @@
+ * release. This is a 24 bit number with 8 bits for major number, 8 bits
+ * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
+ */
+-#define MOD_MD_VERSION_NUM 0x01010f
++#define MOD_MD_VERSION_NUM 0x010110
+
+ #define MD_ACME_DEF_URL "https://acme-v01.api.letsencrypt.org/directory"
+
+--
+2.19.1
+
diff --git a/README.confd b/README.confd
new file mode 100644
index 0000000..f5e9661
--- /dev/null
+++ b/README.confd
@@ -0,0 +1,9 @@
+
+This directory holds configuration files for the Apache HTTP Server;
+any files in this directory which have the ".conf" extension will be
+processed as httpd configuration files. The directory is used in
+addition to the directory /etc/httpd/conf.modules.d/, which contains
+configuration files necessary to load modules.
+
+Files are processed in alphabetical order.
+
diff --git a/README.confmod b/README.confmod
new file mode 100644
index 0000000..d33d1d4
--- /dev/null
+++ b/README.confmod
@@ -0,0 +1,9 @@
+
+This directory holds configuration files for the Apache HTTP Server;
+any files in this directory which have the ".conf" extension will be
+processed as httpd configuration files. This directory contains
+configuration fragments necessary only to load modules.
+Administrators should use the directory "/etc/httpd/conf.d" to modify
+the configuration of httpd, or any modules.
+
+Files are processed in alphanumeric order.
diff --git a/action-configtest.sh b/action-configtest.sh
new file mode 100644
index 0000000..6685b0a
--- /dev/null
+++ b/action-configtest.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec /sbin/apachectl configtest "$@"
diff --git a/action-graceful.sh b/action-graceful.sh
new file mode 100644
index 0000000..dc68b2e
--- /dev/null
+++ b/action-graceful.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec /sbin/apachectl graceful "$@"
diff --git a/htcacheclean.service b/htcacheclean.service
new file mode 100644
index 0000000..166067b
--- /dev/null
+++ b/htcacheclean.service
@@ -0,0 +1,10 @@
+[Unit]
+Description=Disk Cache Cleaning Daemon for Apache HTTP Server
+After=httpd.service
+
+[Service]
+Type=forking
+User=apache
+PIDFile=/run/httpd/htcacheclean/pid
+EnvironmentFile=/etc/sysconfig/htcacheclean
+ExecStart=/usr/sbin/htcacheclean -P /run/httpd/htcacheclean/pid -d $INTERVAL -p $CACHE_ROOT -l $LIMIT $OPTIONS
diff --git a/htcacheclean.sysconf b/htcacheclean.sysconf
new file mode 100644
index 0000000..fffa17b
--- /dev/null
+++ b/htcacheclean.sysconf
@@ -0,0 +1,16 @@
+#
+# Configuration options for systemd service, htcacheclean.service.
+# See htcacheclean(8) for more information on available options.
+#
+
+# Interval between cache clean runs, in minutes
+INTERVAL=15
+
+# Default cache root.
+CACHE_ROOT=/var/cache/httpd/proxy
+
+# Cache size limit in bytes (K=Kbytes, M=Mbytes)
+LIMIT=100M
+
+# Any other options...
+OPTIONS=
diff --git a/httpd-2.4.1-apctl.patch b/httpd-2.4.1-apctl.patch
new file mode 100644
index 0000000..b31c3c5
--- /dev/null
+++ b/httpd-2.4.1-apctl.patch
@@ -0,0 +1,94 @@
+
+- fail gracefully if links is not installed on target system
+- source sysconfig/httpd for custom env. vars etc.
+- make httpd -t work even in SELinux
+- pass $OPTIONS to all $HTTPD invocation
+
+Upstream-HEAD: vendor
+Upstream-2.0: vendor
+Upstream-Status: Vendor-specific changes for better initscript integration
+
+--- httpd-2.4.1/support/apachectl.in.apctl
++++ httpd-2.4.1/support/apachectl.in
+@@ -44,19 +44,25 @@ ARGV="$@"
+ # the path to your httpd binary, including options if necessary
+ HTTPD='@exp_sbindir@/@progname@'
+ #
+-# pick up any necessary environment variables
+-if test -f @exp_sbindir@/envvars; then
+- . @exp_sbindir@/envvars
+-fi
+ #
+ # a command that outputs a formatted text version of the HTML at the
+ # url given on the command line. Designed for lynx, however other
+ # programs may work.
+-LYNX="@LYNX_PATH@ -dump"
++if [ -x "@LYNX_PATH@" ]; then
++ LYNX="@LYNX_PATH@ -dump"
++else
++ LYNX=none
++fi
+ #
+ # the URL to your server's mod_status status page. If you do not
+ # have one, then status and fullstatus will not work.
+ STATUSURL="http://localhost:@PORT@/server-status"
++
++# Source /etc/sysconfig/httpd for $HTTPD setting, etc.
++if [ -r /etc/sysconfig/httpd ]; then
++ . /etc/sysconfig/httpd
++fi
++
+ #
+ # Set this variable to a command that increases the maximum
+ # number of file descriptors allowed per child process. This is
+@@ -76,9 +82,27 @@ if [ "x$ARGV" = "x" ] ; then
+ ARGV="-h"
+ fi
+
++function checklynx() {
++if [ "$LYNX" = "none" ]; then
++ echo "The 'links' package is required for this functionality."
++ exit 8
++fi
++}
++
++function testconfig() {
++# httpd is denied terminal access in SELinux, so run in the
++# current context to get stdout from $HTTPD -t.
++if test -x /usr/sbin/selinuxenabled && /usr/sbin/selinuxenabled; then
++ runcon -- `id -Z` $HTTPD $OPTIONS -t
++else
++ $HTTPD $OPTIONS -t
++fi
++ERROR=$?
++}
++
+ case $ACMD in
+ start|stop|restart|graceful|graceful-stop)
+- $HTTPD -k $ARGV
++ $HTTPD $OPTIONS -k $ARGV
+ ERROR=$?
+ ;;
+ startssl|sslstart|start-SSL)
+@@ -88,17 +112,18 @@ startssl|sslstart|start-SSL)
+ ERROR=2
+ ;;
+ configtest)
+- $HTTPD -t
+- ERROR=$?
++ testconfig
+ ;;
+ status)
++ checklynx
+ $LYNX $STATUSURL | awk ' /process$/ { print; exit } { print } '
+ ;;
+ fullstatus)
++ checklynx
+ $LYNX $STATUSURL
+ ;;
+ *)
+- $HTTPD "$@"
++ $HTTPD $OPTIONS "$@"
+ ERROR=$?
+ esac
+
diff --git a/httpd-2.4.1-corelimit.patch b/httpd-2.4.1-corelimit.patch
new file mode 100644
index 0000000..96f8486
--- /dev/null
+++ b/httpd-2.4.1-corelimit.patch
@@ -0,0 +1,35 @@
+
+Bump up the core size limit if CoreDumpDirectory is
+configured.
+
+Upstream-Status: Was discussed but there are competing desires;
+ there are portability oddities here too.
+
+--- httpd-2.4.1/server/core.c.corelimit
++++ httpd-2.4.1/server/core.c
+@@ -4433,6 +4433,25 @@ static int core_post_config(apr_pool_t *
+ }
+ apr_pool_cleanup_register(pconf, NULL, ap_mpm_end_gen_helper,
+ apr_pool_cleanup_null);
++
++#ifdef RLIMIT_CORE
++ if (ap_coredumpdir_configured) {
++ struct rlimit lim;
++
++ if (getrlimit(RLIMIT_CORE, &lim) == 0 && lim.rlim_cur == 0) {
++ lim.rlim_cur = lim.rlim_max;
++ if (setrlimit(RLIMIT_CORE, &lim) == 0) {
++ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
++ "core dump file size limit raised to %lu bytes",
++ lim.rlim_cur);
++ } else {
++ ap_log_error(APLOG_MARK, APLOG_NOTICE, errno, NULL,
++ "core dump file size is zero, setrlimit failed");
++ }
++ }
++ }
++#endif
++
+ return OK;
+ }
+
diff --git a/httpd-2.4.1-deplibs.patch b/httpd-2.4.1-deplibs.patch
new file mode 100644
index 0000000..b73c21d
--- /dev/null
+++ b/httpd-2.4.1-deplibs.patch
@@ -0,0 +1,19 @@
+
+Link straight against .la files.
+
+Upstream-Status: vendor specific
+
+--- httpd-2.4.1/configure.in.deplibs
++++ httpd-2.4.1/configure.in
+@@ -707,9 +707,9 @@ APACHE_HELP_STRING(--with-suexec-umask,u
+
+ dnl APR should go after the other libs, so the right symbols can be picked up
+ if test x${apu_found} != xobsolete; then
+- AP_LIBS="$AP_LIBS `$apu_config --avoid-ldap --link-libtool --libs`"
++ AP_LIBS="$AP_LIBS `$apu_config --avoid-ldap --link-libtool`"
+ fi
+-AP_LIBS="$AP_LIBS `$apr_config --link-libtool --libs`"
++AP_LIBS="$AP_LIBS `$apr_config --link-libtool`"
+ APACHE_SUBST(AP_LIBS)
+ APACHE_SUBST(AP_BUILD_SRCLIB_DIRS)
+ APACHE_SUBST(AP_CLEAN_SRCLIB_DIRS)
diff --git a/httpd-2.4.17-socket-activation.patch b/httpd-2.4.17-socket-activation.patch
new file mode 100644
index 0000000..dbdd80c
--- /dev/null
+++ b/httpd-2.4.17-socket-activation.patch
@@ -0,0 +1,300 @@
+diff --git a/server/listen.c b/server/listen.c
+index a8e9e6f..1a6c1d3 100644
+--- a/server/listen.c
++++ b/server/listen.c
+@@ -34,6 +34,10 @@
+ #include
+ #endif
+
++#ifdef HAVE_SYSTEMD
++#include
++#endif
++
+ /* we know core's module_index is 0 */
+ #undef APLOG_MODULE_INDEX
+ #define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+@@ -59,9 +63,12 @@ static int ap_listenbacklog;
+ static int ap_listencbratio;
+ static int send_buffer_size;
+ static int receive_buffer_size;
++#ifdef HAVE_SYSTEMD
++static int use_systemd = -1;
++#endif
+
+ /* TODO: make_sock is just begging and screaming for APR abstraction */
+-static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
++static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server, int do_bind_listen)
+ {
+ apr_socket_t *s = server->sd;
+ int one = 1;
+@@ -94,20 +101,6 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
+ return stat;
+ }
+
+-#if APR_HAVE_IPV6
+- if (server->bind_addr->family == APR_INET6) {
+- stat = apr_socket_opt_set(s, APR_IPV6_V6ONLY, v6only_setting);
+- if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
+- ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(00069)
+- "make_sock: for address %pI, apr_socket_opt_set: "
+- "(IPV6_V6ONLY)",
+- server->bind_addr);
+- apr_socket_close(s);
+- return stat;
+- }
+- }
+-#endif
+-
+ /*
+ * To send data over high bandwidth-delay connections at full
+ * speed we must force the TCP window to open wide enough to keep the
+@@ -169,21 +162,37 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
+ }
+ #endif
+
+- if ((stat = apr_socket_bind(s, server->bind_addr)) != APR_SUCCESS) {
+- ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, stat, p, APLOGNO(00072)
+- "make_sock: could not bind to address %pI",
+- server->bind_addr);
+- apr_socket_close(s);
+- return stat;
+- }
++ if (do_bind_listen) {
++#if APR_HAVE_IPV6
++ if (server->bind_addr->family == APR_INET6) {
++ stat = apr_socket_opt_set(s, APR_IPV6_V6ONLY, v6only_setting);
++ if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
++ ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(00069)
++ "make_sock: for address %pI, apr_socket_opt_set: "
++ "(IPV6_V6ONLY)",
++ server->bind_addr);
++ apr_socket_close(s);
++ return stat;
++ }
++ }
++#endif
+
+- if ((stat = apr_socket_listen(s, ap_listenbacklog)) != APR_SUCCESS) {
+- ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, stat, p, APLOGNO(00073)
+- "make_sock: unable to listen for connections "
+- "on address %pI",
+- server->bind_addr);
+- apr_socket_close(s);
+- return stat;
++ if ((stat = apr_socket_bind(s, server->bind_addr)) != APR_SUCCESS) {
++ ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, stat, p, APLOGNO(00072)
++ "make_sock: could not bind to address %pI",
++ server->bind_addr);
++ apr_socket_close(s);
++ return stat;
++ }
++
++ if ((stat = apr_socket_listen(s, ap_listenbacklog)) != APR_SUCCESS) {
++ ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, stat, p, APLOGNO(00073)
++ "make_sock: unable to listen for connections "
++ "on address %pI",
++ server->bind_addr);
++ apr_socket_close(s);
++ return stat;
++ }
+ }
+
+ #ifdef WIN32
+@@ -315,6 +324,123 @@ static int find_listeners(ap_listen_rec **from, ap_listen_rec **to,
+ return found;
+ }
+
++#ifdef HAVE_SYSTEMD
++
++static int find_systemd_socket(process_rec * process, apr_port_t port) {
++ int fdcount, fd;
++ int sdc = sd_listen_fds(0);
++
++ if (sdc < 0) {
++ ap_log_perror(APLOG_MARK, APLOG_CRIT, sdc, process->pool, APLOGNO(02486)
++ "find_systemd_socket: Error parsing enviroment, sd_listen_fds returned %d",
++ sdc);
++ return -1;
++ }
++
++ if (sdc == 0) {
++ ap_log_perror(APLOG_MARK, APLOG_CRIT, sdc, process->pool, APLOGNO(02487)
++ "find_systemd_socket: At least one socket must be set.");
++ return -1;
++ }
++
++ fdcount = atoi(getenv("LISTEN_FDS"));
++ for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + fdcount; fd++) {
++ if (sd_is_socket_inet(fd, 0, 0, -1, port) > 0) {
++ return fd;
++ }
++ }
++
++ return -1;
++}
++
++static apr_status_t alloc_systemd_listener(process_rec * process,
++ int fd, const char *proto,
++ ap_listen_rec **out_rec)
++{
++ apr_status_t rv;
++ struct sockaddr sa;
++ socklen_t len = sizeof(struct sockaddr);
++ apr_os_sock_info_t si;
++ ap_listen_rec *rec;
++ *out_rec = NULL;
++
++ memset(&si, 0, sizeof(si));
++
++ rv = getsockname(fd, &sa, &len);
++
++ if (rv != 0) {
++ rv = apr_get_netos_error();
++ ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, process->pool, APLOGNO(02489)
++ "getsockname on %d failed.", fd);
++ return rv;
++ }
++
++ si.os_sock = &fd;
++ si.family = sa.sa_family;
++ si.local = &sa;
++ si.type = SOCK_STREAM;
++ si.protocol = APR_PROTO_TCP;
++
++ rec = apr_palloc(process->pool, sizeof(ap_listen_rec));
++ rec->active = 0;
++ rec->next = 0;
++
++
++ rv = apr_os_sock_make(&rec->sd, &si, process->pool);
++ if (rv != APR_SUCCESS) {
++ ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, process->pool, APLOGNO(02490)
++ "apr_os_sock_make on %d failed.", fd);
++ return rv;
++ }
++
++ rv = apr_socket_addr_get(&rec->bind_addr, APR_LOCAL, rec->sd);
++ if (rv != APR_SUCCESS) {
++ ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, process->pool, APLOGNO(02491)
++ "apr_socket_addr_get on %d failed.", fd);
++ return rv;
++ }
++
++ rec->protocol = apr_pstrdup(process->pool, proto);
++
++ *out_rec = rec;
++
++ return make_sock(process->pool, rec, 0);
++}
++
++static const char *set_systemd_listener(process_rec *process, apr_port_t port,
++ const char *proto)
++{
++ ap_listen_rec *last, *new;
++ apr_status_t rv;
++ int fd = find_systemd_socket(process, port);
++ if (fd < 0) {
++ return "Systemd socket activation is used, but this port is not "
++ "configured in systemd";
++ }
++
++ last = ap_listeners;
++ while (last && last->next) {
++ last = last->next;
++ }
++
++ rv = alloc_systemd_listener(process, fd, proto, &new);
++ if (rv != APR_SUCCESS) {
++ return "Failed to setup socket passed by systemd using socket activation";
++ }
++
++ if (last == NULL) {
++ ap_listeners = last = new;
++ }
++ else {
++ last->next = new;
++ last = new;
++ }
++
++ return NULL;
++}
++
++#endif /* HAVE_SYSTEMD */
++
+ static const char *alloc_listener(process_rec *process, const char *addr,
+ apr_port_t port, const char* proto,
+ void *slave)
+@@ -495,7 +621,7 @@ static int open_listeners(apr_pool_t *pool)
+ }
+ }
+ #endif
+- if (make_sock(pool, lr) == APR_SUCCESS) {
++ if (make_sock(pool, lr, 1) == APR_SUCCESS) {
+ ++num_open;
+ }
+ else {
+@@ -607,8 +733,28 @@ AP_DECLARE(int) ap_setup_listeners(server_rec *s)
+ }
+ }
+
+- if (open_listeners(s->process->pool)) {
+- return 0;
++#ifdef HAVE_SYSTEMD
++ if (use_systemd) {
++ const char *userdata_key = "ap_open_systemd_listeners";
++ void *data;
++ /* clear the enviroment on our second run
++ * so that none of our future children get confused.
++ */
++ apr_pool_userdata_get(&data, userdata_key, s->process->pool);
++ if (!data) {
++ apr_pool_userdata_set((const void *)1, userdata_key,
++ apr_pool_cleanup_null, s->process->pool);
++ }
++ else {
++ sd_listen_fds(1);
++ }
++ }
++ else
++#endif
++ {
++ if (open_listeners(s->process->pool)) {
++ return 0;
++ }
+ }
+
+ for (lr = ap_listeners; lr; lr = lr->next) {
+@@ -698,7 +844,7 @@ AP_DECLARE(apr_status_t) ap_duplicate_listeners(apr_pool_t *p, server_rec *s,
+ duplr->bind_addr);
+ return stat;
+ }
+- make_sock(p, duplr);
++ make_sock(p, duplr, 1);
+ #if AP_NONBLOCK_WHEN_MULTI_LISTEN
+ use_nonblock = (ap_listeners && ap_listeners->next);
+ stat = apr_socket_opt_set(duplr->sd, APR_SO_NONBLOCK, use_nonblock);
+@@ -825,6 +971,11 @@ AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
+ if (argc < 1 || argc > 2) {
+ return "Listen requires 1 or 2 arguments.";
+ }
++#ifdef HAVE_SYSTEMD
++ if (use_systemd == -1) {
++ use_systemd = sd_listen_fds(0) > 0;
++ }
++#endif
+
+ rv = apr_parse_addr_port(&host, &scope_id, &port, argv[0], cmd->pool);
+ if (rv != APR_SUCCESS) {
+@@ -856,6 +1007,12 @@ AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
+ ap_str_tolower(proto);
+ }
+
++#ifdef HAVE_SYSTEMD
++ if (use_systemd) {
++ return set_systemd_listener(cmd->server->process, port, proto);
++ }
++#endif
++
+ return alloc_listener(cmd->server->process, host, port, proto, NULL);
+ }
+
diff --git a/httpd-2.4.2-icons.patch b/httpd-2.4.2-icons.patch
new file mode 100644
index 0000000..1341999
--- /dev/null
+++ b/httpd-2.4.2-icons.patch
@@ -0,0 +1,26 @@
+
+- Fix config for /icons/ dir to allow symlink to poweredby.png.
+- Avoid using coredump GIF for a directory called "core"
+
+Upstream-Status: vendor specific patch
+
+--- httpd-2.4.2/docs/conf/extra/httpd-autoindex.conf.in.icons
++++ httpd-2.4.2/docs/conf/extra/httpd-autoindex.conf.in
+@@ -21,7 +21,7 @@ IndexOptions FancyIndexing HTMLTable Ver
+ Alias /icons/ "@exp_iconsdir@/"
+
+
+- Options Indexes MultiViews
++ Options Indexes MultiViews FollowSymlinks
+ AllowOverride None
+ Require all granted
+
+@@ -53,7 +53,7 @@ AddIcon /icons/dvi.gif .dvi
+ AddIcon /icons/uuencoded.gif .uu
+ AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl
+ AddIcon /icons/tex.gif .tex
+-AddIcon /icons/bomb.gif core
++AddIcon /icons/bomb.gif core.
+
+ AddIcon /icons/back.gif ..
+ AddIcon /icons/hand.right.gif README
diff --git a/httpd-2.4.25-detect-systemd.patch b/httpd-2.4.25-detect-systemd.patch
new file mode 100644
index 0000000..f8e302b
--- /dev/null
+++ b/httpd-2.4.25-detect-systemd.patch
@@ -0,0 +1,75 @@
+diff -uap httpd-2.4.25/acinclude.m4.detectsystemd httpd-2.4.25/acinclude.m4
+diff -uap httpd-2.4.25/acinclude.m4.detectsystemd httpd-2.4.25/acinclude.m4
+diff -uap httpd-2.4.25/acinclude.m4.detectsystemd httpd-2.4.25/acinclude.m4
+--- httpd-2.4.25/acinclude.m4.detectsystemd
++++ httpd-2.4.25/acinclude.m4
+@@ -604,6 +604,30 @@
+ fi
+ ])
+
++AC_DEFUN(APACHE_CHECK_SYSTEMD, [
++dnl Check for systemd support for listen.c's socket activation.
++case $host in
++*-linux-*)
++ if test -n "$PKGCONFIG" && $PKGCONFIG --exists libsystemd; then
++ SYSTEMD_LIBS=`$PKGCONFIG --libs libsystemd`
++ elif test -n "$PKGCONFIG" && $PKGCONFIG --exists libsystemd-daemon; then
++ SYSTEMD_LIBS=`$PKGCONFIG --libs libsystemd-daemon`
++ else
++ AC_CHECK_LIB(systemd-daemon, sd_notify, SYSTEMD_LIBS="-lsystemd-daemon")
++ fi
++ if test -n "$SYSTEMD_LIBS"; then
++ AC_CHECK_HEADERS(systemd/sd-daemon.h)
++ if test "${ac_cv_header_systemd_sd_daemon_h}" = "no" || test -z "${SYSTEMD_LIBS}"; then
++ AC_MSG_WARN([Your system does not support systemd.])
++ else
++ APR_ADDTO(HTTPD_LIBS, [$SYSTEMD_LIBS])
++ AC_DEFINE(HAVE_SYSTEMD, 1, [Define if systemd is supported])
++ fi
++ fi
++ ;;
++esac
++])
++
+ dnl
+ dnl APACHE_EXPORT_ARGUMENTS
+ dnl Export (via APACHE_SUBST) the various path-related variables that
+diff -uap httpd-2.4.25/configure.in.detectsystemd httpd-2.4.25/configure.in
+--- httpd-2.4.25/configure.in.detectsystemd
++++ httpd-2.4.25/configure.in
+@@ -234,6 +234,7 @@
+ AC_MSG_NOTICE([Using external PCRE library from $PCRE_CONFIG])
+ APR_ADDTO(PCRE_INCLUDES, [`$PCRE_CONFIG --cflags`])
+ APR_ADDTO(PCRE_LIBS, [`$PCRE_CONFIG --libs`])
++ APR_ADDTO(HTTPD_LIBS, [\$(PCRE_LIBS)])
+ else
+ AC_MSG_ERROR([pcre-config for libpcre not found. PCRE is required and available from http://pcre.org/])
+ fi
+@@ -504,6 +510,8 @@
+ AC_DEFINE(HAVE_GMTOFF, 1, [Define if struct tm has a tm_gmtoff field])
+ fi
+
++APACHE_CHECK_SYSTEMD
++
+ dnl ## Set up any appropriate OS-specific environment variables for apachectl
+
+ case $host in
+@@ -668,6 +676,7 @@
+ APACHE_SUBST(BUILTIN_LIBS)
+ APACHE_SUBST(SHLIBPATH_VAR)
+ APACHE_SUBST(OS_SPECIFIC_VARS)
++APACHE_SUBST(HTTPD_LIBS)
+
+ PRE_SHARED_CMDS='echo ""'
+ POST_SHARED_CMDS='echo ""'
+--- httpd-2.4.25/Makefile.in.detectsystemd
++++ httpd-2.4.25/Makefile.in
+@@ -4,7 +4,7 @@
+
+ PROGRAM_NAME = $(progname)
+ PROGRAM_SOURCES = modules.c
+-PROGRAM_LDADD = buildmark.o $(HTTPD_LDFLAGS) $(PROGRAM_DEPENDENCIES) $(PCRE_LIBS) $(EXTRA_LIBS) $(AP_LIBS) $(LIBS)
++PROGRAM_LDADD = buildmark.o $(HTTPD_LDFLAGS) $(PROGRAM_DEPENDENCIES) $(HTTPD_LIBS) $(EXTRA_LIBS) $(AP_LIBS) $(LIBS)
+ PROGRAM_PRELINK = $(COMPILE) -c $(top_srcdir)/server/buildmark.c
+ PROGRAM_DEPENDENCIES = \
+ server/libmain.la \
diff --git a/httpd-2.4.25-selinux.patch b/httpd-2.4.25-selinux.patch
new file mode 100644
index 0000000..fa4614a
--- /dev/null
+++ b/httpd-2.4.25-selinux.patch
@@ -0,0 +1,61 @@
+
+Log the SELinux context at startup.
+
+Upstream-Status: unlikely to be any interest in this upstream
+
+--- httpd-2.4.1/configure.in.selinux
++++ httpd-2.4.1/configure.in
+@@ -458,6 +458,11 @@ fopen64
+ dnl confirm that a void pointer is large enough to store a long integer
+ APACHE_CHECK_VOID_PTR_LEN
+
++AC_CHECK_LIB(selinux, is_selinux_enabled, [
++ AC_DEFINE(HAVE_SELINUX, 1, [Defined if SELinux is supported])
++ APR_ADDTO(HTTPD_LIBS, [-lselinux])
++])
++
+ AC_CACHE_CHECK([for gettid()], ac_cv_gettid,
+ [AC_TRY_RUN(#define _GNU_SOURCE
+ #include
+--- httpd-2.4.1/server/core.c.selinux
++++ httpd-2.4.1/server/core.c
+@@ -58,6 +58,10 @@
+ #include
+ #endif
+
++#ifdef HAVE_SELINUX
++#include
++#endif
++
+ /* LimitRequestBody handling */
+ #define AP_LIMIT_REQ_BODY_UNSET ((apr_off_t) -1)
+ #define AP_DEFAULT_LIMIT_REQ_BODY ((apr_off_t) 0)
+@@ -4452,6 +4456,28 @@ static int core_post_config(apr_pool_t *
+ }
+ #endif
+
++#ifdef HAVE_SELINUX
++ {
++ static int already_warned = 0;
++ int is_enabled = is_selinux_enabled() > 0;
++
++ if (is_enabled && !already_warned) {
++ security_context_t con;
++
++ if (getcon(&con) == 0) {
++
++ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
++ "SELinux policy enabled; "
++ "httpd running as context %s", con);
++
++ already_warned = 1;
++
++ freecon(con);
++ }
++ }
++ }
++#endif
++
+ return OK;
+ }
+
diff --git a/httpd-2.4.3-apctl-systemd.patch b/httpd-2.4.3-apctl-systemd.patch
new file mode 100644
index 0000000..c6bf5da
--- /dev/null
+++ b/httpd-2.4.3-apctl-systemd.patch
@@ -0,0 +1,51 @@
+
+Make apachectl run via systemctl.
+
+Note: "apachectl graceful" is documented to start httpd if not running.
+
+Upstream-Status: vendor specific patch
+
+--- httpd-2.4.18/support/apachectl.in.apctlsystemd
++++ httpd-2.4.18/support/apachectl.in
+@@ -100,9 +100,28 @@ fi
+ ERROR=$?
+ }
+
++if [ "x$2" != "x" ] ; then
++ echo Passing arguments to httpd using apachectl is no longer supported.
++ echo You can only start/stop/restart httpd using this script.
++ echo If you want to pass extra arguments to httpd, edit the
++ echo /etc/sysconfig/httpd config file.
++fi
++
+ case $ACMD in
+-start|stop|restart|graceful|graceful-stop)
+- $HTTPD $OPTIONS -k $ARGV
++start|stop|restart|status)
++ /usr/bin/systemctl $ACMD httpd.service
++ ERROR=$?
++ ;;
++graceful)
++ if /usr/bin/systemctl -q is-active httpd.service; then
++ /usr/bin/systemctl reload httpd.service
++ else
++ /usr/bin/systemctl start httpd.service
++ fi
++ ERROR=$?
++ ;;
++graceful-stop)
++ /usr/bin/systemctl stop httpd.service
+ ERROR=$?
+ ;;
+ startssl|sslstart|start-SSL)
+@@ -114,10 +133,6 @@ startssl|sslstart|start-SSL)
+ configtest)
+ testconfig
+ ;;
+-status)
+- checklynx
+- $LYNX $STATUSURL | awk ' /process$/ { print; exit } { print } '
+- ;;
+ fullstatus)
+ checklynx
+ $LYNX $STATUSURL
diff --git a/httpd-2.4.33-export.patch b/httpd-2.4.33-export.patch
new file mode 100644
index 0000000..9adf398
--- /dev/null
+++ b/httpd-2.4.33-export.patch
@@ -0,0 +1,20 @@
+
+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
+
+--- httpd-2.4.33/server/Makefile.in.export
++++ httpd-2.4.33/server/Makefile.in
+@@ -60,9 +60,6 @@
+ 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/httpd-2.4.33-mddefault.patch b/httpd-2.4.33-mddefault.patch
new file mode 100644
index 0000000..9e82fb8
--- /dev/null
+++ b/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/httpd-2.4.33-r1830819+.patch b/httpd-2.4.33-r1830819+.patch
new file mode 100644
index 0000000..0b2d90d
--- /dev/null
+++ b/httpd-2.4.33-r1830819+.patch
@@ -0,0 +1,690 @@
+# ./pullrev.sh 1830819 1830836 1830912 1830913 1830927 1831168 1831173
+
+http://svn.apache.org/viewvc?view=revision&revision=1830819
+http://svn.apache.org/viewvc?view=revision&revision=1830912
+http://svn.apache.org/viewvc?view=revision&revision=1830913
+http://svn.apache.org/viewvc?view=revision&revision=1830927
+http://svn.apache.org/viewvc?view=revision&revision=1831168
+http://svn.apache.org/viewvc?view=revision&revision=1831173
+http://svn.apache.org/viewvc?view=revision&revision=1835240
+http://svn.apache.org/viewvc?view=revision&revision=1835242
+
+--- httpd-2.4.33/modules/ssl/ssl_engine_config.c.r1830819+
++++ httpd-2.4.33/modules/ssl/ssl_engine_config.c
+@@ -891,7 +891,9 @@
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+- if ((err = ssl_cmd_check_file(cmd, &arg))) {
++ /* Only check for non-ENGINE based certs. */
++ if (!modssl_is_engine_id(arg)
++ && (err = ssl_cmd_check_file(cmd, &arg))) {
+ return err;
+ }
+
+@@ -907,7 +909,9 @@
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ const char *err;
+
+- if ((err = ssl_cmd_check_file(cmd, &arg))) {
++ /* Check keyfile exists for non-ENGINE keys. */
++ if (!modssl_is_engine_id(arg)
++ && (err = ssl_cmd_check_file(cmd, &arg))) {
+ return err;
+ }
+
+--- httpd-2.4.33/modules/ssl/ssl_engine_init.c.r1830819+
++++ httpd-2.4.33/modules/ssl/ssl_engine_init.c
+@@ -1181,12 +1182,18 @@
+ (certfile = APR_ARRAY_IDX(mctx->pks->cert_files, i,
+ const char *));
+ i++) {
++ EVP_PKEY *pkey;
++ const char *engine_certfile = NULL;
++
+ key_id = apr_psprintf(ptemp, "%s:%d", vhost_id, i);
+
+ ERR_clear_error();
+
+ /* first the certificate (public key) */
+- if (mctx->cert_chain) {
++ if (modssl_is_engine_id(certfile)) {
++ engine_certfile = certfile;
++ }
++ else if (mctx->cert_chain) {
+ if ((SSL_CTX_use_certificate_file(mctx->ssl_ctx, certfile,
+ SSL_FILETYPE_PEM) < 1)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02561)
+@@ -1215,12 +1222,46 @@
+
+ ERR_clear_error();
+
+- if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile,
+- SSL_FILETYPE_PEM) < 1) &&
+- (ERR_GET_FUNC(ERR_peek_last_error())
+- != X509_F_X509_CHECK_PRIVATE_KEY)) {
++ if (modssl_is_engine_id(keyfile)) {
++ apr_status_t rv;
++
++ cert = NULL;
++
++ if ((rv = modssl_load_engine_keypair(s, ptemp, vhost_id,
++ engine_certfile, keyfile,
++ &cert, &pkey))) {
++ return rv;
++ }
++
++ if (cert) {
++ if (SSL_CTX_use_certificate(mctx->ssl_ctx, cert) < 1) {
++ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10137)
++ "Failed to configure engine certificate %s, check %s",
++ key_id, certfile);
++ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
++ return APR_EGENERAL;
++ }
++
++ /* SSL_CTX now owns the cert. */
++ X509_free(cert);
++ }
++
++ if (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1) {
++ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10130)
++ "Failed to configure private key %s from engine",
++ keyfile);
++ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
++ return APR_EGENERAL;
++ }
++
++ /* SSL_CTX now owns the key */
++ EVP_PKEY_free(pkey);
++ }
++ else if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile,
++ SSL_FILETYPE_PEM) < 1)
++ && (ERR_GET_FUNC(ERR_peek_last_error())
++ != X509_F_X509_CHECK_PRIVATE_KEY)) {
+ ssl_asn1_t *asn1;
+- EVP_PKEY *pkey;
+ const unsigned char *ptr;
+
+ ERR_clear_error();
+@@ -1307,8 +1348,9 @@
+ /*
+ * Try to read DH parameters from the (first) SSLCertificateFile
+ */
+- if ((certfile = APR_ARRAY_IDX(mctx->pks->cert_files, 0, const char *)) &&
+- (dhparams = ssl_dh_GetParamFromFile(certfile))) {
++ certfile = APR_ARRAY_IDX(mctx->pks->cert_files, 0, const char *);
++ if (certfile && !modssl_is_engine_id(certfile)
++ && (dhparams = ssl_dh_GetParamFromFile(certfile))) {
+ SSL_CTX_set_tmp_dh(mctx->ssl_ctx, dhparams);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02540)
+ "Custom DH parameters (%d bits) for %s loaded from %s",
+@@ -1320,10 +1362,10 @@
+ /*
+ * Similarly, try to read the ECDH curve name from SSLCertificateFile...
+ */
+- if ((certfile != NULL) &&
+- (ecparams = ssl_ec_GetParamFromFile(certfile)) &&
+- (nid = EC_GROUP_get_curve_name(ecparams)) &&
+- (eckey = EC_KEY_new_by_curve_name(nid))) {
++ if (certfile && !modssl_is_engine_id(certfile)
++ && (ecparams = ssl_ec_GetParamFromFile(certfile))
++ && (nid = EC_GROUP_get_curve_name(ecparams))
++ && (eckey = EC_KEY_new_by_curve_name(nid))) {
+ SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, eckey);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02541)
+ "ECDH curve %s for %s specified in %s",
+--- httpd-2.4.33/modules/ssl/ssl_engine_pphrase.c.r1830819+
++++ httpd-2.4.33/modules/ssl/ssl_engine_pphrase.c
+@@ -143,9 +143,6 @@
+ const char *key_id = asn1_table_vhost_key(mc, p, sc->vhost_id, idx);
+ EVP_PKEY *pPrivateKey = NULL;
+ ssl_asn1_t *asn1;
+- unsigned char *ucp;
+- long int length;
+- BOOL bReadable;
+ int nPassPhrase = (*pphrases)->nelts;
+ int nPassPhraseRetry = 0;
+ apr_time_t pkey_mtime = 0;
+@@ -222,16 +219,12 @@
+ * is not empty. */
+ ERR_clear_error();
+
+- bReadable = ((pPrivateKey = modssl_read_privatekey(ppcb_arg.pkey_file,
+- NULL, ssl_pphrase_Handle_CB, &ppcb_arg)) != NULL ?
+- TRUE : FALSE);
+-
+- /*
+- * when the private key file now was readable,
+- * it's fine and we go out of the loop
+- */
+- if (bReadable)
+- break;
++ pPrivateKey = modssl_read_privatekey(ppcb_arg.pkey_file,
++ ssl_pphrase_Handle_CB, &ppcb_arg);
++ /* If the private key was successfully read, nothing more to
++ do here. */
++ if (pPrivateKey != NULL)
++ break;
+
+ /*
+ * when we have more remembered pass phrases
+@@ -356,19 +349,12 @@
+ nPassPhrase++;
+ }
+
+- /*
+- * Insert private key into the global module configuration
+- * (we convert it to a stand-alone DER byte sequence
+- * because the SSL library uses static variables inside a
+- * RSA structure which do not survive DSO reloads!)
+- */
+- length = i2d_PrivateKey(pPrivateKey, NULL);
+- ucp = ssl_asn1_table_set(mc->tPrivateKey, key_id, length);
+- (void)i2d_PrivateKey(pPrivateKey, &ucp); /* 2nd arg increments */
++ /* Cache the private key in the global module configuration so it
++ * can be used after subsequent reloads. */
++ asn1 = ssl_asn1_table_set(mc->tPrivateKey, key_id, pPrivateKey);
+
+ if (ppcb_arg.nPassPhraseDialogCur != 0) {
+ /* remember mtime of encrypted keys */
+- asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id);
+ asn1->source_mtime = pkey_mtime;
+ }
+
+@@ -619,3 +605,288 @@
+ */
+ return (len);
+ }
++
++
++#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
++
++/* OpenSSL UI implementation for passphrase entry; largely duplicated
++ * from ssl_pphrase_Handle_CB but adjusted for UI API. TODO: Might be
++ * worth trying to shift pphrase handling over to the UI API
++ * completely. */
++static int passphrase_ui_open(UI *ui)
++{
++ pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
++ SSLSrvConfigRec *sc = mySrvConfig(ppcb->s);
++
++ ppcb->nPassPhraseDialog++;
++ ppcb->nPassPhraseDialogCur++;
++
++ /*
++ * Builtin or Pipe dialog
++ */
++ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
++ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
++ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
++ if (!readtty) {
++ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s,
++ APLOGNO(10143)
++ "Init: Creating pass phrase dialog pipe child "
++ "'%s'", sc->server->pphrase_dialog_path);
++ if (ssl_pipe_child_create(ppcb->p,
++ sc->server->pphrase_dialog_path)
++ != APR_SUCCESS) {
++ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s,
++ APLOGNO(10144)
++ "Init: Failed to create pass phrase pipe '%s'",
++ sc->server->pphrase_dialog_path);
++ return 0;
++ }
++ }
++ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10145)
++ "Init: Requesting pass phrase via piped dialog");
++ }
++ else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
++#ifdef WIN32
++ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s, APLOGNO(10146)
++ "Init: Failed to create pass phrase pipe '%s'",
++ sc->server->pphrase_dialog_path);
++ return 0;
++#else
++ /*
++ * stderr has already been redirected to the error_log.
++ * rather than attempting to temporarily rehook it to the terminal,
++ * we print the prompt to stdout before EVP_read_pw_string turns
++ * off tty echo
++ */
++ apr_file_open_stdout(&writetty, ppcb->p);
++
++ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10147)
++ "Init: Requesting pass phrase via builtin terminal "
++ "dialog");
++#endif
++ }
++
++ /*
++ * The first time display a header to inform the user about what
++ * program he actually speaks to, which module is responsible for
++ * this terminal dialog and why to the hell he has to enter
++ * something...
++ */
++ if (ppcb->nPassPhraseDialog == 1) {
++ apr_file_printf(writetty, "%s mod_ssl (Pass Phrase Dialog)\n",
++ AP_SERVER_BASEVERSION);
++ apr_file_printf(writetty,
++ "A pass phrase is required to access the private key.\n");
++ }
++ if (ppcb->bPassPhraseDialogOnce) {
++ ppcb->bPassPhraseDialogOnce = FALSE;
++ apr_file_printf(writetty, "\n");
++ apr_file_printf(writetty, "Private key %s (%s)\n",
++ ppcb->key_id, ppcb->pkey_file);
++ }
++ }
++
++ return 1;
++}
++
++static int passphrase_ui_read(UI *ui, UI_STRING *uis)
++{
++ pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
++ SSLSrvConfigRec *sc = mySrvConfig(ppcb->s);
++ const char *prompt;
++ int i;
++ int bufsize;
++ int len;
++ char *buf;
++
++ prompt = UI_get0_output_string(uis);
++ if (prompt == NULL) {
++ prompt = "Enter pass phrase:";
++ }
++
++ /*
++ * Get the maximum expected size and allocate the buffer
++ */
++ bufsize = UI_get_result_maxsize(uis);
++ buf = apr_pcalloc(ppcb->p, bufsize);
++
++ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
++ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
++ /*
++ * Get the pass phrase through a callback.
++ * Empty input is not accepted.
++ */
++ for (;;) {
++ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
++ i = pipe_get_passwd_cb(buf, bufsize, "", FALSE);
++ }
++ else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */
++ i = EVP_read_pw_string(buf, bufsize, "", FALSE);
++ }
++ if (i != 0) {
++ OPENSSL_cleanse(buf, bufsize);
++ return 0;
++ }
++ len = strlen(buf);
++ if (len < 1){
++ apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase"
++ "empty (needs to be at least 1 character).\n");
++ apr_file_puts(prompt, writetty);
++ }
++ else {
++ break;
++ }
++ }
++ }
++ /*
++ * Filter program
++ */
++ else if (sc->server->pphrase_dialog_type == SSL_PPTYPE_FILTER) {
++ const char *cmd = sc->server->pphrase_dialog_path;
++ const char **argv = apr_palloc(ppcb->p, sizeof(char *) * 3);
++ char *result;
++
++ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10148)
++ "Init: Requesting pass phrase from dialog filter "
++ "program (%s)", cmd);
++
++ argv[0] = cmd;
++ argv[1] = ppcb->key_id;
++ argv[2] = NULL;
++
++ result = ssl_util_readfilter(ppcb->s, ppcb->p, cmd, argv);
++ apr_cpystrn(buf, result, bufsize);
++ len = strlen(buf);
++ }
++
++ /*
++ * Ok, we now have the pass phrase, so give it back
++ */
++ ppcb->cpPassPhraseCur = apr_pstrdup(ppcb->p, buf);
++ UI_set_result(ui, uis, buf);
++
++ /* Clear sensitive data. */
++ OPENSSL_cleanse(buf, bufsize);
++ return 1;
++}
++
++static int passphrase_ui_write(UI *ui, UI_STRING *uis)
++{
++ pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui);
++ SSLSrvConfigRec *sc;
++ const char *prompt;
++
++ sc = mySrvConfig(ppcb->s);
++
++ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN
++ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) {
++ prompt = UI_get0_output_string(uis);
++ apr_file_puts(prompt, writetty);
++ }
++
++ return 1;
++}
++
++static int passphrase_ui_close(UI *ui)
++{
++ /*
++ * Close the pipes if they were opened
++ */
++ if (readtty) {
++ apr_file_close(readtty);
++ apr_file_close(writetty);
++ readtty = writetty = NULL;
++ }
++ return 1;
++}
++
++static apr_status_t pp_ui_method_cleanup(void *uip)
++{
++ UI_METHOD *uim = uip;
++
++ UI_destroy_method(uim);
++
++ return APR_SUCCESS;
++}
++
++static UI_METHOD *get_passphrase_ui(apr_pool_t *p)
++{
++ UI_METHOD *ui_method = UI_create_method("Passphrase UI");
++
++ UI_method_set_opener(ui_method, passphrase_ui_open);
++ UI_method_set_reader(ui_method, passphrase_ui_read);
++ UI_method_set_writer(ui_method, passphrase_ui_write);
++ UI_method_set_closer(ui_method, passphrase_ui_close);
++
++ apr_pool_cleanup_register(p, ui_method, pp_ui_method_cleanup,
++ pp_ui_method_cleanup);
++
++ return ui_method;
++}
++
++
++apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p,
++ const char *vhostid,
++ const char *certid, const char *keyid,
++ X509 **pubkey, EVP_PKEY **privkey)
++{
++ SSLModConfigRec *mc = myModConfig(s);
++ ENGINE *e;
++ UI_METHOD *ui_method = get_passphrase_ui(p);
++ pphrase_cb_arg_t ppcb;
++
++ memset(&ppcb, 0, sizeof ppcb);
++ ppcb.s = s;
++ ppcb.p = p;
++ ppcb.bPassPhraseDialogOnce = TRUE;
++ ppcb.key_id = vhostid;
++ ppcb.pkey_file = keyid;
++
++ if (!mc->szCryptoDevice) {
++ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10131)
++ "Init: Cannot load private key `%s' without engine",
++ keyid);
++ return ssl_die(s);
++ }
++
++ if (!(e = ENGINE_by_id(mc->szCryptoDevice))) {
++ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10132)
++ "Init: Failed to load Crypto Device API `%s'",
++ mc->szCryptoDevice);
++ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
++ return ssl_die(s);
++ }
++
++ if (APLOGdebug(s)) {
++ ENGINE_ctrl_cmd_string(e, "VERBOSE", NULL, 0);
++ }
++
++ if (certid) {
++ struct {
++ const char *cert_id;
++ X509 *cert;
++ } params = { certid, NULL };
++
++ if (!ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, ¶ms, NULL, 1)) {
++ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10136)
++ "Init: Unable to get the certificate");
++ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
++ return ssl_die(s);
++ }
++
++ *pubkey = params.cert;
++ }
++
++ *privkey = ENGINE_load_private_key(e, keyid, ui_method, &ppcb);
++ if (*privkey == NULL) {
++ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10133)
++ "Init: Unable to get the private key");
++ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
++ return ssl_die(s);
++ }
++
++ ENGINE_free(e);
++
++ return APR_SUCCESS;
++}
++#endif
+--- httpd-2.4.33/modules/ssl/ssl_private.h.r1830819+
++++ httpd-2.4.33/modules/ssl/ssl_private.h
+@@ -976,21 +976,28 @@
+ apr_status_t ssl_load_encrypted_pkey(server_rec *, apr_pool_t *, int,
+ const char *, apr_array_header_t **);
+
++/* Load public and/or private key from the configured ENGINE. Private
++ * key returned as *pkey. certid can be NULL, in which case *pubkey
++ * is not altered. Errors logged on failure. */
++apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p,
++ const char *vhostid,
++ const char *certid, const char *keyid,
++ X509 **pubkey, EVP_PKEY **privkey);
++
+ /** Diffie-Hellman Parameter Support */
+ DH *ssl_dh_GetParamFromFile(const char *);
+ #ifdef HAVE_ECC
+ EC_GROUP *ssl_ec_GetParamFromFile(const char *);
+ #endif
+
+-unsigned char *ssl_asn1_table_set(apr_hash_t *table,
+- const char *key,
+- long int length);
+-
+-ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table,
+- const char *key);
+-
+-void ssl_asn1_table_unset(apr_hash_t *table,
+- const char *key);
++/* Store the EVP_PKEY key (serialized into DER) in the hash table with
++ * key, returning the ssl_asn1_t structure pointer. */
++ssl_asn1_t *ssl_asn1_table_set(apr_hash_t *table, const char *key,
++ EVP_PKEY *pkey);
++/* Retrieve the ssl_asn1_t structure with given key from the hash. */
++ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table, const char *key);
++/* Remove and free the ssl_asn1_t structure with given key. */
++void ssl_asn1_table_unset(apr_hash_t *table, const char *key);
+
+ /** Mutex Support */
+ int ssl_mutex_init(server_rec *, apr_pool_t *);
+@@ -1078,6 +1085,10 @@
+ int ssl_is_challenge(conn_rec *c, const char *servername,
+ X509 **pcert, EVP_PKEY **pkey);
+
++/* Returns non-zero if the cert/key filename should be handled through
++ * the configured ENGINE. */
++int modssl_is_engine_id(const char *name);
++
+ #endif /* SSL_PRIVATE_H */
+ /** @} */
+
+--- httpd-2.4.33/modules/ssl/ssl_util.c.r1830819+
++++ httpd-2.4.33/modules/ssl/ssl_util.c
+@@ -181,45 +181,37 @@
+ return TRUE;
+ }
+
+-/*
+- * certain key data needs to survive restarts,
+- * which are stored in the user data table of s->process->pool.
+- * to prevent "leaking" of this data, we use malloc/free
+- * rather than apr_palloc and these wrappers to help make sure
+- * we do not leak the malloc-ed data.
+- */
+-unsigned char *ssl_asn1_table_set(apr_hash_t *table,
+- const char *key,
+- long int length)
++/* Decrypted private keys are cached to survive restarts. The cached
++ * data must have lifetime of the process (hence malloc/free rather
++ * than pools), and uses raw DER since the EVP_PKEY structure
++ * internals may not survive across a module reload. */
++ssl_asn1_t *ssl_asn1_table_set(apr_hash_t *table, const char *key,
++ EVP_PKEY *pkey)
+ {
+ apr_ssize_t klen = strlen(key);
+ ssl_asn1_t *asn1 = apr_hash_get(table, key, klen);
++ apr_size_t length = i2d_PrivateKey(pkey, NULL);
++ unsigned char *p;
+
+- /*
+- * if a value for this key already exists,
+- * reuse as much of the already malloc-ed data
+- * as possible.
+- */
++ /* Re-use structure if cached previously. */
+ if (asn1) {
+ if (asn1->nData != length) {
+- free(asn1->cpData); /* XXX: realloc? */
+- asn1->cpData = NULL;
++ asn1->cpData = ap_realloc(asn1->cpData, length);
+ }
+ }
+ else {
+ asn1 = ap_malloc(sizeof(*asn1));
+ asn1->source_mtime = 0; /* used as a note for encrypted private keys */
+- asn1->cpData = NULL;
+- }
+-
+- asn1->nData = length;
+- if (!asn1->cpData) {
+ asn1->cpData = ap_malloc(length);
++
++ apr_hash_set(table, key, klen, asn1);
+ }
+
+- apr_hash_set(table, key, klen, asn1);
++ asn1->nData = length;
++ p = asn1->cpData;
++ i2d_PrivateKey(pkey, &p); /* increases p by length */
+
+- return asn1->cpData; /* caller will assign a value to this */
++ return asn1;
+ }
+
+ ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table,
+@@ -469,3 +461,13 @@
+ }
+
+ #endif /* #if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API */
++
++int modssl_is_engine_id(const char *name)
++{
++#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT)
++ /* ### Can handle any other special ENGINE key names here? */
++ return strncmp(name, "pkcs11:", 7) == 0;
++#else
++ return 0;
++#endif
++}
+--- httpd-2.4.33/modules/ssl/ssl_util_ssl.c.r1830819+
++++ httpd-2.4.33/modules/ssl/ssl_util_ssl.c
+@@ -74,7 +74,7 @@
+ ** _________________________________________________________________
+ */
+
+-EVP_PKEY *modssl_read_privatekey(const char* filename, EVP_PKEY **key, pem_password_cb *cb, void *s)
++EVP_PKEY *modssl_read_privatekey(const char *filename, pem_password_cb *cb, void *s)
+ {
+ EVP_PKEY *rc;
+ BIO *bioS;
+@@ -83,7 +83,7 @@
+ /* 1. try PEM (= DER+Base64+headers) */
+ if ((bioS=BIO_new_file(filename, "r")) == NULL)
+ return NULL;
+- rc = PEM_read_bio_PrivateKey(bioS, key, cb, s);
++ rc = PEM_read_bio_PrivateKey(bioS, NULL, cb, s);
+ BIO_free(bioS);
+
+ if (rc == NULL) {
+@@ -107,41 +107,9 @@
+ BIO_free(bioS);
+ }
+ }
+- if (rc != NULL && key != NULL) {
+- if (*key != NULL)
+- EVP_PKEY_free(*key);
+- *key = rc;
+- }
+ return rc;
+ }
+
+-typedef struct {
+- const char *pass;
+- int pass_len;
+-} pass_ctx;
+-
+-static int provide_pass(char *buf, int size, int rwflag, void *baton)
+-{
+- pass_ctx *ctx = baton;
+- if (ctx->pass_len > 0) {
+- if (ctx->pass_len < size) {
+- size = (int)ctx->pass_len;
+- }
+- memcpy(buf, ctx->pass, size);
+- }
+- return ctx->pass_len;
+-}
+-
+-EVP_PKEY *modssl_read_encrypted_pkey(const char *filename, EVP_PKEY **key,
+- const char *pass, apr_size_t pass_len)
+-{
+- pass_ctx ctx;
+-
+- ctx.pass = pass;
+- ctx.pass_len = pass_len;
+- return modssl_read_privatekey(filename, key, provide_pass, &ctx);
+-}
+-
+ /* _________________________________________________________________
+ **
+ ** Smart shutdown
+--- httpd-2.4.33/modules/ssl/ssl_util_ssl.h.r1830819+
++++ httpd-2.4.33/modules/ssl/ssl_util_ssl.h
+@@ -64,8 +64,11 @@
+ void modssl_init_app_data2_idx(void);
+ void *modssl_get_app_data2(SSL *);
+ void modssl_set_app_data2(SSL *, void *);
+-EVP_PKEY *modssl_read_privatekey(const char *, EVP_PKEY **, pem_password_cb *, void *);
+-EVP_PKEY *modssl_read_encrypted_pkey(const char *, EVP_PKEY **, const char *, apr_size_t);
++
++/* Read private key from filename in either PEM or raw base64(DER)
++ * format, using password entry callback cb and userdata. */
++EVP_PKEY *modssl_read_privatekey(const char *filename, pem_password_cb *cb, void *ud);
++
+ int modssl_smart_shutdown(SSL *ssl);
+ BOOL modssl_X509_getBC(X509 *, int *, int *);
+ char *modssl_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne,
diff --git a/httpd-2.4.33-sslmultiproxy.patch b/httpd-2.4.33-sslmultiproxy.patch
new file mode 100644
index 0000000..679f229
--- /dev/null
+++ b/httpd-2.4.33-sslmultiproxy.patch
@@ -0,0 +1,126 @@
+From ce2d1d7d4b2bebe34cf37fdeb30d35050092c5b5 Mon Sep 17 00:00:00 2001
+From: Rob Crittenden
+Date: Thu, 12 Apr 2018 14:36:28 -0400
+Subject: [PATCH] httpd-2.4.18-sslmultiproxy.patch
+
+---
+ modules/ssl/mod_ssl.c | 24 ++++++++++++++++++++++--
+ modules/ssl/ssl_engine_vars.c | 18 +++++++++++++++++-
+ 2 files changed, 39 insertions(+), 3 deletions(-)
+
+diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c
+index 48d64cb..42e85a3 100644
+diff -uap httpd-2.4.33/modules/ssl/mod_ssl.c.sslmultiproxy httpd-2.4.33/modules/ssl/mod_ssl.c
+--- httpd-2.4.33/modules/ssl/mod_ssl.c.sslmultiproxy
++++ httpd-2.4.33/modules/ssl/mod_ssl.c
+@@ -444,12 +444,19 @@
+ return OK;
+ }
+
++static APR_OPTIONAL_FN_TYPE(ssl_engine_disable) *othermod_engine_disable;
++static APR_OPTIONAL_FN_TYPE(ssl_engine_set) *othermod_engine_set;
++
+ static SSLConnRec *ssl_init_connection_ctx(conn_rec *c,
+ ap_conf_vector_t *per_dir_config)
+ {
+ SSLConnRec *sslconn = myConnConfig(c);
+ SSLSrvConfigRec *sc;
+
++ if (othermod_engine_disable) {
++ othermod_engine_disable(c);
++ }
++
+ if (sslconn) {
+ return sslconn;
+ }
+@@ -508,6 +515,10 @@
+ {
+ SSLConnRec *sslconn;
+ int status;
++
++ if (othermod_engine_set) {
++ return othermod_engine_set(c, per_dir_config, proxy, enable);
++ }
+
+ if (proxy) {
+ sslconn = ssl_init_connection_ctx(c, per_dir_config);
+@@ -537,12 +548,18 @@
+
+ static int ssl_proxy_enable(conn_rec *c)
+ {
+- return ssl_engine_set(c, NULL, 1, 1);
++ if (othermod_engine_set)
++ return othermod_engine_set(c, NULL, 1, 1);
++ else
++ return ssl_engine_set(c, NULL, 1, 1);
+ }
+
+ static int ssl_engine_disable(conn_rec *c)
+ {
+- return ssl_engine_set(c, NULL, 0, 0);
++ if (othermod_engine_set)
++ return othermod_engine_set(c, NULL, 0, 0);
++ else
++ return ssl_engine_set(c, NULL, 0, 0);
+ }
+
+ int ssl_init_ssl_connection(conn_rec *c, request_rec *r)
+@@ -730,6 +747,9 @@
+ APR_HOOK_MIDDLE);
+
+ ssl_var_register(p);
++
++ othermod_engine_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
++ othermod_engine_set = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_set);
+
+ APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable);
+ APR_REGISTER_OPTIONAL_FN(ssl_engine_disable);
+diff -uap httpd-2.4.33/modules/ssl/ssl_engine_vars.c.sslmultiproxy httpd-2.4.33/modules/ssl/ssl_engine_vars.c
+--- httpd-2.4.33/modules/ssl/ssl_engine_vars.c.sslmultiproxy
++++ httpd-2.4.33/modules/ssl/ssl_engine_vars.c
+@@ -54,6 +54,8 @@
+ static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize);
+ static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var);
+ static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl);
++static APR_OPTIONAL_FN_TYPE(ssl_is_https) *othermod_is_https;
++static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *othermod_var_lookup;
+
+ static SSLConnRec *ssl_get_effective_config(conn_rec *c)
+ {
+@@ -68,7 +70,9 @@
+ static int ssl_is_https(conn_rec *c)
+ {
+ SSLConnRec *sslconn = ssl_get_effective_config(c);
+- return sslconn && sslconn->ssl;
++
++ return (sslconn && sslconn->ssl)
++ || (othermod_is_https && othermod_is_https(c));
+ }
+
+ static const char var_interface[] = "mod_ssl/" AP_SERVER_BASEREVISION;
+@@ -137,6 +141,9 @@
+ {
+ char *cp, *cp2;
+
++ othermod_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
++ othermod_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
++
+ APR_REGISTER_OPTIONAL_FN(ssl_is_https);
+ APR_REGISTER_OPTIONAL_FN(ssl_var_lookup);
+ APR_REGISTER_OPTIONAL_FN(ssl_ext_list);
+@@ -271,6 +278,15 @@
+ */
+ if (result == NULL && c != NULL) {
+ SSLConnRec *sslconn = ssl_get_effective_config(c);
++
++ if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)
++ && (!sslconn || !sslconn->ssl) && othermod_var_lookup) {
++ /* For an SSL_* variable, if mod_ssl is not enabled for
++ * this connection and another SSL module is present, pass
++ * through to that module. */
++ return othermod_var_lookup(p, s, c, r, var);
++ }
++
+ if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)
+ && sslconn && sslconn->ssl)
+ result = ssl_var_lookup_ssl(p, sslconn, r, var+4);
diff --git a/httpd-2.4.33-systemd.patch b/httpd-2.4.33-systemd.patch
new file mode 100644
index 0000000..7f5ee3b
--- /dev/null
+++ b/httpd-2.4.33-systemd.patch
@@ -0,0 +1,245 @@
+--- httpd-2.4.33/modules/arch/unix/config5.m4.systemd
++++ httpd-2.4.33/modules/arch/unix/config5.m4
+@@ -18,6 +18,16 @@
+ fi
+ ])
+
++APACHE_MODULE(systemd, Systemd support, , , all, [
++ if test "${ac_cv_header_systemd_sd_daemon_h}" = "no" || test -z "${SYSTEMD_LIBS}"; then
++ AC_MSG_WARN([Your system does not support systemd.])
++ enable_systemd="no"
++ else
++ APR_ADDTO(MOD_SYSTEMD_LDADD, [$SYSTEMD_LIBS])
++ enable_systemd="yes"
++ fi
++])
++
+ APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current])
+
+ APACHE_MODPATH_FINISH
+--- httpd-2.4.33/modules/arch/unix/mod_systemd.c.systemd
++++ httpd-2.4.33/modules/arch/unix/mod_systemd.c
+@@ -0,0 +1,223 @@
++/* 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.
++ *
++ */
++
++#include
++#include
++#include "ap_mpm.h"
++#include
++#include
++#include
++#include
++#include
++#include
++#include "unixd.h"
++#include "scoreboard.h"
++#include "mpm_common.h"
++
++#include "systemd/sd-daemon.h"
++#include "systemd/sd-journal.h"
++
++#if APR_HAVE_UNISTD_H
++#include
++#endif
++
++static int shutdown_timer = 0;
++static int shutdown_counter = 0;
++static unsigned long bytes_served;
++static pid_t mainpid;
++static char describe_listeners[50];
++
++static int systemd_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
++ apr_pool_t *ptemp)
++{
++ sd_notify(0,
++ "RELOADING=1\n"
++ "STATUS=Reading configuration...\n");
++ ap_extended_status = 1;
++ return OK;
++}
++
++static char *dump_listener(ap_listen_rec *lr, apr_pool_t *p)
++{
++ apr_sockaddr_t *sa = lr->bind_addr;
++ char addr[128];
++
++ if (apr_sockaddr_is_wildcard(sa)) {
++ return apr_pstrcat(p, "port ", apr_itoa(p, sa->port), NULL);
++ }
++
++ apr_sockaddr_ip_getbuf(addr, sizeof addr, sa);
++
++ return apr_psprintf(p, "%s port %u", addr, sa->port);
++}
++
++static int systemd_post_config(apr_pool_t *pconf, apr_pool_t *plog,
++ apr_pool_t *ptemp, server_rec *s)
++{
++ ap_listen_rec *lr;
++ apr_size_t plen = sizeof describe_listeners;
++ char *p = describe_listeners;
++
++ if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG)
++ return OK;
++
++ for (lr = ap_listeners; lr; lr = lr->next) {
++ char *s = dump_listener(lr, ptemp);
++
++ if (strlen(s) + 3 < plen) {
++ char *newp = apr_cpystrn(p, s, plen);
++ if (lr->next)
++ newp = apr_cpystrn(newp, ", ", 3);
++ plen -= newp - p;
++ p = newp;
++ }
++ else {
++ if (plen < 4) {
++ p = describe_listeners + sizeof describe_listeners - 4;
++ plen = 4;
++ }
++ apr_cpystrn(p, "...", plen);
++ break;
++ }
++ }
++
++ sd_journal_print(LOG_INFO, "Server configured, listening on: %s", describe_listeners);
++
++ return OK;
++}
++
++static int systemd_pre_mpm(apr_pool_t *p, ap_scoreboard_e sb_type)
++{
++ int rv;
++
++ mainpid = getpid();
++
++ rv = sd_notifyf(0, "READY=1\n"
++ "STATUS=Started, listening on: %s\n"
++ "MAINPID=%" APR_PID_T_FMT,
++ describe_listeners, mainpid);
++ if (rv < 0) {
++ ap_log_perror(APLOG_MARK, APLOG_ERR, 0, p, APLOGNO(02395)
++ "sd_notifyf returned an error %d", rv);
++ }
++
++ return OK;
++}
++
++static int systemd_monitor(apr_pool_t *p, server_rec *s)
++{
++ ap_sload_t sload;
++ apr_interval_time_t up_time;
++ char bps[5];
++ int rv;
++
++ if (!ap_extended_status) {
++ /* Nothing useful to report if ExtendedStatus disabled. */
++ return DECLINED;
++ }
++
++ ap_get_sload(&sload);
++
++ if (sload.access_count == 0) {
++ rv = sd_notifyf(0, "READY=1\n"
++ "STATUS=Running, listening on: %s\n",
++ describe_listeners);
++ }
++ else {
++ /* up_time in seconds */
++ up_time = (apr_uint32_t) apr_time_sec(apr_time_now() -
++ ap_scoreboard_image->global->restart_time);
++
++ apr_strfsize((unsigned long)((float) (sload.bytes_served)
++ / (float) up_time), bps);
++
++ rv = sd_notifyf(0, "READY=1\n"
++ "STATUS=Total requests: %lu; Idle/Busy workers %d/%d;"
++ "Requests/sec: %.3g; Bytes served/sec: %sB/sec\n",
++ sload.access_count, sload.idle, sload.busy,
++ ((float) sload.access_count) / (float) up_time, bps);
++ }
++
++ if (rv < 0) {
++ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02396)
++ "sd_notifyf returned an error %d", rv);
++ }
++
++ /* Shutdown httpd when nothing is sent for shutdown_timer seconds. */
++ if (sload.bytes_served == bytes_served) {
++ /* mpm_common.c: INTERVAL_OF_WRITABLE_PROBES is 10 */
++ shutdown_counter += 10;
++ if (shutdown_timer > 0 && shutdown_counter >= shutdown_timer) {
++ rv = sd_notifyf(0, "READY=1\n"
++ "STATUS=Stopped as result of IdleShutdown "
++ "timeout.");
++ if (rv < 0) {
++ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02804)
++ "sd_notifyf returned an error %d", rv);
++ }
++ kill(mainpid, AP_SIG_GRACEFUL);
++ }
++ }
++ else {
++ shutdown_counter = 0;
++ }
++
++ bytes_served = sload.bytes_served;
++
++ return DECLINED;
++}
++
++static void systemd_register_hooks(apr_pool_t *p)
++{
++ /* Enable ap_extended_status. */
++ ap_hook_pre_config(systemd_pre_config, NULL, NULL, APR_HOOK_LAST);
++ /* Grab the listener config. */
++ ap_hook_post_config(systemd_post_config, NULL, NULL, APR_HOOK_LAST);
++ /* We know the PID in this hook ... */
++ ap_hook_pre_mpm(systemd_pre_mpm, NULL, NULL, APR_HOOK_LAST);
++ /* Used to update httpd's status line using sd_notifyf */
++ ap_hook_monitor(systemd_monitor, NULL, NULL, APR_HOOK_MIDDLE);
++}
++
++static const char *set_shutdown_timer(cmd_parms *cmd, void *dummy,
++ const char *arg)
++{
++ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
++ if (err != NULL) {
++ return err;
++ }
++
++ shutdown_timer = atoi(arg);
++ return NULL;
++}
++
++static const command_rec systemd_cmds[] =
++{
++AP_INIT_TAKE1("IdleShutdown", set_shutdown_timer, NULL, RSRC_CONF,
++ "Number of seconds in idle-state after which httpd is shutdown"),
++ {NULL}
++};
++
++AP_DECLARE_MODULE(systemd) = {
++ STANDARD20_MODULE_STUFF,
++ NULL,
++ NULL,
++ NULL,
++ NULL,
++ systemd_cmds,
++ systemd_register_hooks,
++};
diff --git a/httpd-2.4.34-enable-sslv3.patch b/httpd-2.4.34-enable-sslv3.patch
new file mode 100644
index 0000000..f559bf9
--- /dev/null
+++ b/httpd-2.4.34-enable-sslv3.patch
@@ -0,0 +1,60 @@
+diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c
+index 517ce30..075f7e1 100644
+--- a/modules/ssl/ssl_engine_config.c
++++ b/modules/ssl/ssl_engine_config.c
+@@ -1474,6 +1474,8 @@ static const char *ssl_cmd_protocol_parse(cmd_parms *parms,
+ #endif
+ else if (strcEQ(w, "all")) {
+ thisopt = SSL_PROTOCOL_ALL;
++ // by default, ALL kw doesn't turn on SSLv3
++ thisopt &= ~SSL_PROTOCOL_SSLV3;
+ }
+ else {
+ return apr_pstrcat(parms->temp_pool,
+diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
+index 60df45f..f6645c2 100644
+--- a/modules/ssl/ssl_engine_init.c
++++ b/modules/ssl/ssl_engine_init.c
+@@ -537,6 +537,28 @@ static apr_status_t ssl_init_ctx_tls_extensions(server_rec *s,
+ }
+ #endif
+
++/*
++ * Enable/disable SSLProtocol. If the mod_ssl enables protocol
++ * which is disabled by default by OpenSSL, show a warning.
++ * "option" is for example SSL_OP_NO_SSLv3.
++ */
++static void ssl_set_ctx_protocol_option(server_rec *s,
++ SSL_CTX *ctx,
++ long option,
++ int enabled,
++ const char *name)
++{
++ if (!enabled) {
++ SSL_CTX_set_options(ctx, option);
++ }
++ else if (SSL_CTX_get_options(ctx) & option) {
++ SSL_CTX_clear_options(ctx, option);
++ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(02904)
++ "Allowing SSLProtocol %s even though it is disabled "
++ "by OpenSSL by default on this system", name);
++ }
++}
++
+ static apr_status_t ssl_init_ctx_protocol(server_rec *s,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+@@ -695,9 +719,13 @@ static apr_status_t ssl_init_ctx_protocol(server_rec *s,
+ }
+ if (prot == TLS1_1_VERSION && protocol & SSL_PROTOCOL_TLSV1) {
+ prot = TLS1_VERSION;
++ ssl_set_ctx_protocol_option(s, ctx, SSL_OP_NO_TLSv1,
++ protocol & SSL_PROTOCOL_TLSV1, "TLSv1");
+ }
+ #ifndef OPENSSL_NO_SSL3
+ if (prot == TLS1_VERSION && protocol & SSL_PROTOCOL_SSLV3) {
++ ssl_set_ctx_protocol_option(s, ctx, SSL_OP_NO_SSLv3,
++ protocol & SSL_PROTOCOL_SSLV3, "SSLv3");
+ prot = SSL3_VERSION;
+ }
+ #endif
diff --git a/httpd-2.4.34-layfix.patch b/httpd-2.4.34-layfix.patch
new file mode 100644
index 0000000..39728a1
--- /dev/null
+++ b/httpd-2.4.34-layfix.patch
@@ -0,0 +1,24 @@
+--- httpd-2.4.34/config.layout.layfix
++++ httpd-2.4.34/config.layout
+@@ -133,6 +133,7 @@
+ # Layout used in Fedora httpd packaging.
+
+ prefix: /usr
++ localstatedir: /var
+ exec_prefix: ${prefix}
+ bindir: ${prefix}/bin
+ sbindir: ${prefix}/sbin
+@@ -144,11 +145,10 @@
+ installbuilddir: ${libdir}/httpd/build
+ errordir: ${datadir}/error
+ iconsdir: ${datadir}/icons
+- htdocsdir: /var/www/html
++ htdocsdir: ${localstatedir}/www/html
+ manualdir: ${datadir}/manual
+- cgidir: /var/www/cgi-bin
++ cgidir: ${localstatedir}/www/cgi-bin
+ includedir: ${prefix}/include/httpd
+- localstatedir: /var
+ runtimedir: /run/httpd
+ logfiledir: ${localstatedir}/log/httpd
+ proxycachedir: ${localstatedir}/cache/httpd/proxy
diff --git a/httpd-2.4.34-r1555631.patch b/httpd-2.4.34-r1555631.patch
new file mode 100644
index 0000000..7ca9478
--- /dev/null
+++ b/httpd-2.4.34-r1555631.patch
@@ -0,0 +1,14 @@
+# ./pullrev.sh 1555631
+http://svn.apache.org/viewvc?view=revision&revision=1555631
+
+--- httpd-2.4.34/modules/ssl/ssl_engine_ocsp.c
++++ httpd-2.4.34/modules/ssl/ssl_engine_ocsp.c
+@@ -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/httpd-2.4.34-r1738878.patch b/httpd-2.4.34-r1738878.patch
new file mode 100644
index 0000000..5af48f5
--- /dev/null
+++ b/httpd-2.4.34-r1738878.patch
@@ -0,0 +1,130 @@
+--- httpd-2.4.34/modules/proxy/ajp_header.c.r1738878
++++ httpd-2.4.34/modules/proxy/ajp_header.c
+@@ -213,7 +213,8 @@
+
+ static apr_status_t ajp_marshal_into_msgb(ajp_msg_t *msg,
+ request_rec *r,
+- apr_uri_t *uri)
++ apr_uri_t *uri,
++ const char *secret)
+ {
+ int method;
+ apr_uint32_t i, num_headers = 0;
+@@ -293,17 +294,15 @@
+ i, elts[i].key, elts[i].val);
+ }
+
+-/* XXXX need to figure out how to do this
+- if (s->secret) {
++ if (secret) {
+ if (ajp_msg_append_uint8(msg, SC_A_SECRET) ||
+- ajp_msg_append_string(msg, s->secret)) {
++ ajp_msg_append_string(msg, secret)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(03228)
+- "Error ajp_marshal_into_msgb - "
++ "ajp_marshal_into_msgb: "
+ "Error appending secret");
+ return APR_EGENERAL;
+ }
+ }
+- */
+
+ if (r->user) {
+ if (ajp_msg_append_uint8(msg, SC_A_REMOTE_USER) ||
+@@ -671,7 +670,8 @@
+ apr_status_t ajp_send_header(apr_socket_t *sock,
+ request_rec *r,
+ apr_size_t buffsize,
+- apr_uri_t *uri)
++ apr_uri_t *uri,
++ const char *secret)
+ {
+ ajp_msg_t *msg;
+ apr_status_t rc;
+@@ -683,7 +683,7 @@
+ return rc;
+ }
+
+- rc = ajp_marshal_into_msgb(msg, r, uri);
++ rc = ajp_marshal_into_msgb(msg, r, uri, secret);
+ if (rc != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00988)
+ "ajp_send_header: ajp_marshal_into_msgb failed");
+--- httpd-2.4.34/modules/proxy/ajp.h.r1738878
++++ httpd-2.4.34/modules/proxy/ajp.h
+@@ -413,12 +413,14 @@
+ * @param sock backend socket
+ * @param r current request
+ * @param buffsize max size of the AJP packet.
++ * @param secret authentication secret
+ * @param uri requested uri
+ * @return APR_SUCCESS or error
+ */
+ apr_status_t ajp_send_header(apr_socket_t *sock, request_rec *r,
+ apr_size_t buffsize,
+- apr_uri_t *uri);
++ apr_uri_t *uri,
++ const char *secret);
+
+ /**
+ * Read the ajp message and return the type of the message.
+--- httpd-2.4.34/modules/proxy/mod_proxy_ajp.c.r1738878
++++ httpd-2.4.34/modules/proxy/mod_proxy_ajp.c
+@@ -193,6 +193,7 @@
+ apr_off_t content_length = 0;
+ int original_status = r->status;
+ const char *original_status_line = r->status_line;
++ const char *secret = NULL;
+
+ if (psf->io_buffer_size_set)
+ maxsize = psf->io_buffer_size;
+@@ -202,12 +203,15 @@
+ maxsize = AJP_MSG_BUFFER_SZ;
+ maxsize = APR_ALIGN(maxsize, 1024);
+
++ if (*conn->worker->s->secret)
++ secret = conn->worker->s->secret;
++
+ /*
+ * Send the AJP request to the remote server
+ */
+
+ /* send request headers */
+- status = ajp_send_header(conn->sock, r, maxsize, uri);
++ status = ajp_send_header(conn->sock, r, maxsize, uri, secret);
+ if (status != APR_SUCCESS) {
+ conn->close = 1;
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00868)
+--- httpd-2.4.34/modules/proxy/mod_proxy.c.r1738878
++++ httpd-2.4.34/modules/proxy/mod_proxy.c
+@@ -319,6 +319,12 @@
+ (int)sizeof(worker->s->upgrade));
+ }
+ }
++ else if (!strcasecmp(key, "secret")) {
++ if (PROXY_STRNCPY(worker->s->secret, val) != APR_SUCCESS) {
++ return apr_psprintf(p, "Secret length must be < %d characters",
++ (int)sizeof(worker->s->secret));
++ }
++ }
+ else if (!strcasecmp(key, "responsefieldsize")) {
+ long s = atol(val);
+ if (s < 0) {
+--- httpd-2.4.34/modules/proxy/mod_proxy.h.r1738878
++++ httpd-2.4.34/modules/proxy/mod_proxy.h
+@@ -357,6 +357,7 @@
+ #define PROXY_WORKER_MAX_HOSTNAME_SIZE 64
+ #define PROXY_BALANCER_MAX_HOSTNAME_SIZE PROXY_WORKER_MAX_HOSTNAME_SIZE
+ #define PROXY_BALANCER_MAX_STICKY_SIZE 64
++#define PROXY_WORKER_MAX_SECRET_SIZE 64
+
+ #define PROXY_RFC1035_HOSTNAME_SIZE 256
+
+@@ -453,6 +454,7 @@
+ char hostname_ex[PROXY_RFC1035_HOSTNAME_SIZE]; /* RFC1035 compliant version of the remote backend address */
+ apr_size_t response_field_size; /* Size of proxy response buffer in bytes. */
+ unsigned int response_field_size_set:1;
++ char secret[PROXY_WORKER_MAX_SECRET_SIZE]; /* authentication secret (e.g. AJP13) */
+ } proxy_worker_shared;
+
+ #define ALIGNED_PROXY_WORKER_SHARED_SIZE (APR_ALIGN_DEFAULT(sizeof(proxy_worker_shared)))
diff --git a/httpd-2.4.34-r1827912+.patch b/httpd-2.4.34-r1827912+.patch
new file mode 100644
index 0000000..98c7ac8
--- /dev/null
+++ b/httpd-2.4.34-r1827912+.patch
@@ -0,0 +1,858 @@
+
+Pull all changes from upstream integration branch:
+
+svn diff -r1840105:1841219 https://svn.apache.org/repos/asf/httpd/httpd/branches/tlsv1.3-for-2.4.x
+
+--- httpd-2.4.34/modules/ssl/mod_ssl.c.r1827912+
++++ httpd-2.4.34/modules/ssl/mod_ssl.c
+@@ -93,9 +93,9 @@
+ SSL_CMD_SRV(FIPS, FLAG,
+ "Enable FIPS-140 mode "
+ "(`on', `off')")
+- SSL_CMD_ALL(CipherSuite, TAKE1,
+- "Colon-delimited list of permitted SSL Ciphers "
+- "('XXX:...:XXX' - see manual)")
++ SSL_CMD_ALL(CipherSuite, TAKE12,
++ "Colon-delimited list of permitted SSL Ciphers, optional preceeded "
++ "by protocol identifier ('XXX:...:XXX' - see manual)")
+ SSL_CMD_SRV(CertificateFile, TAKE1,
+ "SSL Server Certificate file "
+ "('/path/to/file' - PEM or DER encoded)")
+@@ -185,9 +185,9 @@
+ SSL_CMD_PXY(ProxyProtocol, RAW_ARGS,
+ "SSL Proxy: enable or disable SSL protocol flavors "
+ "('[+-][" SSL_PROTOCOLS "] ...' - see manual)")
+- SSL_CMD_PXY(ProxyCipherSuite, TAKE1,
++ SSL_CMD_PXY(ProxyCipherSuite, TAKE12,
+ "SSL Proxy: colon-delimited list of permitted SSL ciphers "
+- "('XXX:...:XXX' - see manual)")
++ ", optionally preceeded by protocol specifier ('XXX:...:XXX' - see manual)")
+ SSL_CMD_PXY(ProxyVerify, TAKE1,
+ "SSL Proxy: whether to verify the remote certificate "
+ "('on' or 'off')")
+@@ -398,7 +398,7 @@
+ /* We must register the library in full, to ensure our configuration
+ * code can successfully test the SSL environment.
+ */
+-#if MODSSL_USE_OPENSSL_PRE_1_1_API
++#if MODSSL_USE_OPENSSL_PRE_1_1_API || defined(LIBRESSL_VERSION_NUMBER)
+ (void)CRYPTO_malloc_init();
+ #else
+ OPENSSL_malloc_init();
+--- httpd-2.4.34/modules/ssl/ssl_engine_config.c.r1827912+
++++ httpd-2.4.34/modules/ssl/ssl_engine_config.c
+@@ -136,6 +136,7 @@
+ mctx->auth.cipher_suite = NULL;
+ mctx->auth.verify_depth = UNSET;
+ mctx->auth.verify_mode = SSL_CVERIFY_UNSET;
++ mctx->auth.tls13_ciphers = NULL;
+
+ mctx->ocsp_mask = UNSET;
+ mctx->ocsp_force_default = UNSET;
+@@ -280,6 +281,7 @@
+ cfgMergeString(auth.cipher_suite);
+ cfgMergeInt(auth.verify_depth);
+ cfgMerge(auth.verify_mode, SSL_CVERIFY_UNSET);
++ cfgMergeString(auth.tls13_ciphers);
+
+ cfgMergeInt(ocsp_mask);
+ cfgMergeBool(ocsp_force_default);
+@@ -761,22 +763,37 @@
+
+ const char *ssl_cmd_SSLCipherSuite(cmd_parms *cmd,
+ void *dcfg,
+- const char *arg)
++ const char *arg1, const char *arg2)
+ {
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+
+- /* always disable null and export ciphers */
+- arg = apr_pstrcat(cmd->pool, arg, ":!aNULL:!eNULL:!EXP", NULL);
+-
+- if (cmd->path) {
+- dc->szCipherSuite = arg;
++ if (arg2 == NULL) {
++ arg2 = arg1;
++ arg1 = "SSL";
+ }
+- else {
+- sc->server->auth.cipher_suite = arg;
++
++ if (!strcmp("SSL", arg1)) {
++ /* always disable null and export ciphers */
++ arg2 = apr_pstrcat(cmd->pool, arg2, ":!aNULL:!eNULL:!EXP", NULL);
++ if (cmd->path) {
++ dc->szCipherSuite = arg2;
++ }
++ else {
++ sc->server->auth.cipher_suite = arg2;
++ }
++ return NULL;
+ }
+-
+- return NULL;
++#if SSL_HAVE_PROTOCOL_TLSV1_3
++ else if (!strcmp("TLSv1.3", arg1)) {
++ if (cmd->path) {
++ return "TLSv1.3 ciphers cannot be set inside a directory context";
++ }
++ sc->server->auth.tls13_ciphers = arg2;
++ return NULL;
++ }
++#endif
++ return apr_pstrcat(cmd->pool, "procotol '", arg1, "' not supported", NULL);
+ }
+
+ #define SSL_FLAGS_CHECK_FILE \
+@@ -1449,6 +1466,9 @@
+ else if (strcEQ(w, "TLSv1.2")) {
+ thisopt = SSL_PROTOCOL_TLSV1_2;
+ }
++ else if (SSL_HAVE_PROTOCOL_TLSV1_3 && strcEQ(w, "TLSv1.3")) {
++ thisopt = SSL_PROTOCOL_TLSV1_3;
++ }
+ #endif
+ else if (strcEQ(w, "all")) {
+ thisopt = SSL_PROTOCOL_ALL;
+@@ -1510,16 +1530,28 @@
+
+ const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *cmd,
+ void *dcfg,
+- const char *arg)
++ const char *arg1, const char *arg2)
+ {
+ SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
+-
+- /* always disable null and export ciphers */
+- arg = apr_pstrcat(cmd->pool, arg, ":!aNULL:!eNULL:!EXP", NULL);
+-
+- dc->proxy->auth.cipher_suite = arg;
+-
+- return NULL;
++
++ if (arg2 == NULL) {
++ arg2 = arg1;
++ arg1 = "SSL";
++ }
++
++ if (!strcmp("SSL", arg1)) {
++ /* always disable null and export ciphers */
++ arg2 = apr_pstrcat(cmd->pool, arg2, ":!aNULL:!eNULL:!EXP", NULL);
++ dc->proxy->auth.cipher_suite = arg2;
++ return NULL;
++ }
++#if SSL_HAVE_PROTOCOL_TLSV1_3
++ else if (!strcmp("TLSv1.3", arg1)) {
++ dc->proxy->auth.tls13_ciphers = arg2;
++ return NULL;
++ }
++#endif
++ return apr_pstrcat(cmd->pool, "procotol '", arg1, "' not supported", NULL);
+ }
+
+ const char *ssl_cmd_SSLProxyVerify(cmd_parms *cmd,
+--- httpd-2.4.34/modules/ssl/ssl_engine_init.c.r1827912+
++++ httpd-2.4.34/modules/ssl/ssl_engine_init.c
+@@ -568,6 +568,9 @@
+ #ifdef HAVE_TLSV1_X
+ (protocol & SSL_PROTOCOL_TLSV1_1 ? "TLSv1.1, " : ""),
+ (protocol & SSL_PROTOCOL_TLSV1_2 ? "TLSv1.2, " : ""),
++#if SSL_HAVE_PROTOCOL_TLSV1_3
++ (protocol & SSL_PROTOCOL_TLSV1_3 ? "TLSv1.3, " : ""),
++#endif
+ #endif
+ NULL);
+ cp[strlen(cp)-2] = NUL;
+@@ -600,6 +603,13 @@
+ TLSv1_2_client_method() : /* proxy */
+ TLSv1_2_server_method(); /* server */
+ }
++#if SSL_HAVE_PROTOCOL_TLSV1_3
++ else if (protocol == SSL_PROTOCOL_TLSV1_3) {
++ method = mctx->pkp ?
++ TLSv1_3_client_method() : /* proxy */
++ TLSv1_3_server_method(); /* server */
++ }
++#endif
+ #endif
+ else { /* For multiple protocols, we need a flexible method */
+ method = mctx->pkp ?
+@@ -617,7 +627,8 @@
+
+ SSL_CTX_set_options(ctx, SSL_OP_ALL);
+
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
++#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
++ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20800000L)
+ /* always disable SSLv2, as per RFC 6176 */
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
+
+@@ -639,10 +650,19 @@
+ if (!(protocol & SSL_PROTOCOL_TLSV1_2)) {
+ SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);
+ }
++#if SSL_HAVE_PROTOCOL_TLSV1_3
++ ssl_set_ctx_protocol_option(s, ctx, SSL_OP_NO_TLSv1_3,
++ protocol & SSL_PROTOCOL_TLSV1_3, "TLSv1.3");
++#endif
+ #endif
+
+ #else /* #if OPENSSL_VERSION_NUMBER < 0x10100000L */
+ /* We first determine the maximum protocol version we should provide */
++#if SSL_HAVE_PROTOCOL_TLSV1_3
++ if (SSL_HAVE_PROTOCOL_TLSV1_3 && (protocol & SSL_PROTOCOL_TLSV1_3)) {
++ prot = TLS1_3_VERSION;
++ } else
++#endif
+ if (protocol & SSL_PROTOCOL_TLSV1_2) {
+ prot = TLS1_2_VERSION;
+ } else if (protocol & SSL_PROTOCOL_TLSV1_1) {
+@@ -664,6 +684,11 @@
+
+ /* Next we scan for the minimal protocol version we should provide,
+ * but we do not allow holes between max and min */
++#if SSL_HAVE_PROTOCOL_TLSV1_3
++ if (prot == TLS1_3_VERSION && protocol & SSL_PROTOCOL_TLSV1_2) {
++ prot = TLS1_2_VERSION;
++ }
++#endif
+ if (prot == TLS1_2_VERSION && protocol & SSL_PROTOCOL_TLSV1_1) {
+ prot = TLS1_1_VERSION;
+ }
+@@ -736,6 +761,13 @@
+ SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
+ #endif
+
++#if OPENSSL_VERSION_NUMBER >= 0x1010100fL
++ /* For OpenSSL >=1.1.1, disable auto-retry mode so it's possible
++ * to consume handshake records without blocking for app-data.
++ * https://github.com/openssl/openssl/issues/7178 */
++ SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY);
++#endif
++
+ return APR_SUCCESS;
+ }
+
+@@ -888,7 +920,15 @@
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+-
++#if SSL_HAVE_PROTOCOL_TLSV1_3
++ if (mctx->auth.tls13_ciphers
++ && !SSL_CTX_set_ciphersuites(ctx, mctx->auth.tls13_ciphers)) {
++ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO()
++ "Unable to configure permitted TLSv1.3 ciphers");
++ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
++ return ssl_die(s);
++ }
++#endif
+ return APR_SUCCESS;
+ }
+
+@@ -1493,6 +1533,13 @@
+ X509_STORE_CTX *sctx;
+ X509_STORE *store = SSL_CTX_get_cert_store(mctx->ssl_ctx);
+
++#if OPENSSL_VERSION_NUMBER >= 0x1010100fL
++ /* For OpenSSL >=1.1.1, turn on client cert support which is
++ * otherwise turned off by default (by design).
++ * https://github.com/openssl/openssl/issues/6933 */
++ SSL_CTX_set_post_handshake_auth(mctx->ssl_ctx, 1);
++#endif
++
+ SSL_CTX_set_client_cert_cb(mctx->ssl_ctx,
+ ssl_callback_proxy_cert);
+
+--- httpd-2.4.34/modules/ssl/ssl_engine_kernel.c.r1827912+
++++ httpd-2.4.34/modules/ssl/ssl_engine_kernel.c
+@@ -188,6 +188,12 @@
+ || strcmp(a1->cipher_suite, a2->cipher_suite))) {
+ return 0;
+ }
++ /* both have the same ca cipher suite string */
++ if ((a1->tls13_ciphers != a2->tls13_ciphers)
++ && (!a1->tls13_ciphers || !a2->tls13_ciphers
++ || strcmp(a1->tls13_ciphers, a2->tls13_ciphers))) {
++ return 0;
++ }
+ return 1;
+ }
+
+@@ -424,87 +430,70 @@
+ }
+ }
+
+-/*
+- * Access Handler
+- */
+-int ssl_hook_Access(request_rec *r)
++static int ssl_check_post_client_verify(request_rec *r, SSLSrvConfigRec *sc,
++ SSLDirConfigRec *dc, SSLConnRec *sslconn,
++ SSL *ssl)
+ {
+- SSLDirConfigRec *dc = myDirConfig(r);
+- SSLSrvConfigRec *sc = mySrvConfig(r->server);
+- SSLConnRec *sslconn = myConnConfig(r->connection);
+- SSL *ssl = sslconn ? sslconn->ssl : NULL;
+- server_rec *handshakeserver = sslconn ? sslconn->server : NULL;
+- SSLSrvConfigRec *hssc = handshakeserver? mySrvConfig(handshakeserver) : NULL;
+- SSL_CTX *ctx = NULL;
+- apr_array_header_t *requires;
+- ssl_require_t *ssl_requires;
+- int ok, i;
+- BOOL renegotiate = FALSE, renegotiate_quick = FALSE;
+ X509 *cert;
+- X509 *peercert;
+- X509_STORE *cert_store = NULL;
+- X509_STORE_CTX *cert_store_ctx;
+- STACK_OF(SSL_CIPHER) *cipher_list_old = NULL, *cipher_list = NULL;
+- const SSL_CIPHER *cipher = NULL;
+- int depth, verify_old, verify, n, is_slave = 0;
+- const char *ncipher_suite;
+-
+- /* On a slave connection, we do not expect to have an SSLConnRec, but
+- * our master connection might have one. */
+- if (!(sslconn && ssl) && r->connection->master) {
+- sslconn = myConnConfig(r->connection->master);
+- ssl = sslconn ? sslconn->ssl : NULL;
+- handshakeserver = sslconn ? sslconn->server : NULL;
+- hssc = handshakeserver? mySrvConfig(handshakeserver) : NULL;
+- is_slave = 1;
+- }
+
+- if (ssl) {
+- /*
+- * We should have handshaken here (on handshakeserver),
+- * otherwise we are being redirected (ErrorDocument) from
+- * a renegotiation failure below. The access is still
+- * forbidden in the latter case, let ap_die() handle
+- * this recursive (same) error.
+- */
+- if (!SSL_is_init_finished(ssl)) {
+- return HTTP_FORBIDDEN;
++ /*
++ * Remember the peer certificate's DN
++ */
++ if ((cert = SSL_get_peer_certificate(ssl))) {
++ if (sslconn->client_cert) {
++ X509_free(sslconn->client_cert);
+ }
+- ctx = SSL_get_SSL_CTX(ssl);
++ sslconn->client_cert = cert;
++ sslconn->client_dn = NULL;
+ }
+-
++
+ /*
+- * Support for SSLRequireSSL directive
++ * Finally check for acceptable renegotiation results
+ */
+- if (dc->bSSLRequired && !ssl) {
+- if ((sc->enabled == SSL_ENABLED_OPTIONAL) && !is_slave) {
+- /* This vhost was configured for optional SSL, just tell the
+- * client that we need to upgrade.
+- */
+- apr_table_setn(r->err_headers_out, "Upgrade", "TLS/1.0, HTTP/1.1");
+- apr_table_setn(r->err_headers_out, "Connection", "Upgrade");
++ if ((dc->nVerifyClient != SSL_CVERIFY_NONE) ||
++ (sc->server->auth.verify_mode != SSL_CVERIFY_NONE)) {
++ BOOL do_verify = ((dc->nVerifyClient == SSL_CVERIFY_REQUIRE) ||
++ (sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE));
++
++ if (do_verify && (SSL_get_verify_result(ssl) != X509_V_OK)) {
++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02262)
++ "Re-negotiation handshake failed: "
++ "Client verification failed");
+
+- return HTTP_UPGRADE_REQUIRED;
++ return HTTP_FORBIDDEN;
+ }
+
+- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02219)
+- "access to %s failed, reason: %s",
+- r->filename, "SSL connection required");
+-
+- /* remember forbidden access for strict require option */
+- apr_table_setn(r->notes, "ssl-access-forbidden", "1");
++ if (do_verify) {
++ if (cert == NULL) {
++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02263)
++ "Re-negotiation handshake failed: "
++ "Client certificate missing");
+
+- return HTTP_FORBIDDEN;
++ return HTTP_FORBIDDEN;
++ }
++ }
+ }
++ return OK;
++}
+
+- /*
+- * Check to see whether SSL is in use; if it's not, then no
+- * further access control checks are relevant. (the test for
+- * sc->enabled is probably strictly unnecessary)
+- */
+- if (sc->enabled == SSL_ENABLED_FALSE || !ssl) {
+- return DECLINED;
+- }
++/*
++ * Access Handler, classic flavour, for SSL/TLS up to v1.2
++ * where everything can be renegotiated and no one is happy.
++ */
++static int ssl_hook_Access_classic(request_rec *r, SSLSrvConfigRec *sc, SSLDirConfigRec *dc,
++ SSLConnRec *sslconn, SSL *ssl)
++{
++ server_rec *handshakeserver = sslconn ? sslconn->server : NULL;
++ SSLSrvConfigRec *hssc = handshakeserver? mySrvConfig(handshakeserver) : NULL;
++ SSL_CTX *ctx = NULL;
++ BOOL renegotiate = FALSE, renegotiate_quick = FALSE;
++ X509 *peercert;
++ X509_STORE *cert_store = NULL;
++ X509_STORE_CTX *cert_store_ctx;
++ STACK_OF(SSL_CIPHER) *cipher_list_old = NULL, *cipher_list = NULL;
++ const SSL_CIPHER *cipher = NULL;
++ int depth, verify_old, verify, n, rc;
++ const char *ncipher_suite;
+
+ #ifdef HAVE_SRP
+ /*
+@@ -581,7 +570,7 @@
+ }
+
+ /* configure new state */
+- if (is_slave) {
++ if (r->connection->master) {
+ /* TODO: this categorically fails changed cipher suite settings
+ * on slave connections. We could do better by
+ * - create a new SSL* from our SSL_CTX and set cipher suite there,
+@@ -659,7 +648,7 @@
+ }
+
+ if (renegotiate) {
+- if (is_slave) {
++ if (r->connection->master) {
+ /* The request causes renegotiation on a slave connection.
+ * This is not allowed since we might have concurrent requests
+ * on this connection.
+@@ -732,7 +721,7 @@
+ (verify & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)))
+ {
+ renegotiate = TRUE;
+- if (is_slave) {
++ if (r->connection->master) {
+ /* The request causes renegotiation on a slave connection.
+ * This is not allowed since we might have concurrent requests
+ * on this connection.
+@@ -885,6 +874,7 @@
+
+ if (renegotiate_quick) {
+ STACK_OF(X509) *cert_stack;
++ X509 *cert;
+
+ /* perform just a manual re-verification of the peer */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02258)
+@@ -1037,43 +1027,10 @@
+ }
+
+ /*
+- * Remember the peer certificate's DN
+- */
+- if ((cert = SSL_get_peer_certificate(ssl))) {
+- if (sslconn->client_cert) {
+- X509_free(sslconn->client_cert);
+- }
+- sslconn->client_cert = cert;
+- sslconn->client_dn = NULL;
+- }
+-
+- /*
+ * Finally check for acceptable renegotiation results
+ */
+- if ((dc->nVerifyClient != SSL_CVERIFY_NONE) ||
+- (sc->server->auth.verify_mode != SSL_CVERIFY_NONE)) {
+- BOOL do_verify = ((dc->nVerifyClient == SSL_CVERIFY_REQUIRE) ||
+- (sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE));
+-
+- if (do_verify && (SSL_get_verify_result(ssl) != X509_V_OK)) {
+- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02262)
+- "Re-negotiation handshake failed: "
+- "Client verification failed");
+-
+- return HTTP_FORBIDDEN;
+- }
+-
+- if (do_verify) {
+- if ((peercert = SSL_get_peer_certificate(ssl)) == NULL) {
+- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02263)
+- "Re-negotiation handshake failed: "
+- "Client certificate missing");
+-
+- return HTTP_FORBIDDEN;
+- }
+-
+- X509_free(peercert);
+- }
++ if (OK != (rc = ssl_check_post_client_verify(r, sc, dc, sslconn, ssl))) {
++ return rc;
+ }
+
+ /*
+@@ -1096,6 +1053,215 @@
+ }
+ }
+
++ return DECLINED;
++}
++
++#if SSL_HAVE_PROTOCOL_TLSV1_3
++/*
++ * Access Handler, modern flavour, for SSL/TLS v1.3 and onward.
++ * Only client certificates can be requested, everything else stays.
++ */
++static int ssl_hook_Access_modern(request_rec *r, SSLSrvConfigRec *sc, SSLDirConfigRec *dc,
++ SSLConnRec *sslconn, SSL *ssl)
++{
++ if ((dc->nVerifyClient != SSL_CVERIFY_UNSET) ||
++ (sc->server->auth.verify_mode != SSL_CVERIFY_UNSET)) {
++ int vmode_inplace, vmode_needed;
++ int change_vmode = FALSE;
++ int old_state, n, rc;
++
++ vmode_inplace = SSL_get_verify_mode(ssl);
++ vmode_needed = SSL_VERIFY_NONE;
++
++ if ((dc->nVerifyClient == SSL_CVERIFY_REQUIRE) ||
++ (sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE)) {
++ vmode_needed |= SSL_VERIFY_PEER_STRICT;
++ }
++
++ if ((dc->nVerifyClient == SSL_CVERIFY_OPTIONAL) ||
++ (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA) ||
++ (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL) ||
++ (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA))
++ {
++ vmode_needed |= SSL_VERIFY_PEER;
++ }
++
++ if (vmode_needed == SSL_VERIFY_NONE) {
++ return DECLINED;
++ }
++
++ vmode_needed |= SSL_VERIFY_CLIENT_ONCE;
++ if (vmode_inplace != vmode_needed) {
++ /* Need to change, if new setting is more restrictive than existing one */
++
++ if ((vmode_inplace == SSL_VERIFY_NONE)
++ || (!(vmode_inplace & SSL_VERIFY_PEER)
++ && (vmode_needed & SSL_VERIFY_PEER))
++ || (!(vmode_inplace & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
++ && (vmode_needed & SSL_VERIFY_FAIL_IF_NO_PEER_CERT))) {
++ /* need to change the effective verify mode */
++ change_vmode = TRUE;
++ }
++ else {
++ /* FIXME: does this work with TLSv1.3? Is this more than re-inspecting
++ * the certificate we should already have? */
++ /*
++ * override of SSLVerifyDepth
++ *
++ * The depth checks are handled by us manually inside the
++ * verify callback function and not by OpenSSL internally
++ * (and our function is aware of both the per-server and
++ * per-directory contexts). So we cannot ask OpenSSL about
++ * the currently verify depth. Instead we remember it in our
++ * SSLConnRec attached to the SSL* of OpenSSL. We've to force
++ * the renegotiation if the reconfigured/new verify depth is
++ * less than the currently active/remembered verify depth
++ * (because this means more restriction on the certificate
++ * chain).
++ */
++ n = (sslconn->verify_depth != UNSET)?
++ sslconn->verify_depth : sc->server->auth.verify_depth;
++ /* determine the new depth */
++ sslconn->verify_depth = (dc->nVerifyDepth != UNSET)
++ ? dc->nVerifyDepth
++ : sc->server->auth.verify_depth;
++ if (sslconn->verify_depth < n) {
++ change_vmode = TRUE;
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
++ "Reduced client verification depth will "
++ "force renegotiation");
++ }
++ }
++ }
++
++ if (change_vmode) {
++ char peekbuf[1];
++
++ if (r->connection->master) {
++ /* FIXME: modifying the SSL on a slave connection is no good.
++ * We would need to push this back to the master connection
++ * somehow.
++ */
++ apr_table_setn(r->notes, "ssl-renegotiate-forbidden", "verify-client");
++ return HTTP_FORBIDDEN;
++ }
++
++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO() "verify client post handshake");
++
++ SSL_set_verify(ssl, vmode_needed, ssl_callback_SSLVerify);
++
++ if (SSL_verify_client_post_handshake(ssl) != 1) {
++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10158)
++ "cannot perform post-handshake authentication");
++ ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, r->server);
++ apr_table_setn(r->notes, "error-notes",
++ "Reason: Cannot perform Post-Handshake Authentication.
");
++ return HTTP_FORBIDDEN;
++ }
++
++ old_state = sslconn->reneg_state;
++ sslconn->reneg_state = RENEG_ALLOW;
++ modssl_set_app_data2(ssl, r);
++
++ SSL_do_handshake(ssl);
++ /* Need to trigger renegotiation handshake by reading.
++ * Peeking 0 bytes actually works.
++ * See: http://marc.info/?t=145493359200002&r=1&w=2
++ */
++ SSL_peek(ssl, peekbuf, 0);
++
++ sslconn->reneg_state = old_state;
++ modssl_set_app_data2(ssl, NULL);
++
++ /*
++ * Finally check for acceptable renegotiation results
++ */
++ if (OK != (rc = ssl_check_post_client_verify(r, sc, dc, sslconn, ssl))) {
++ return rc;
++ }
++ }
++ }
++
++ return DECLINED;
++}
++#endif
++
++int ssl_hook_Access(request_rec *r)
++{
++ SSLDirConfigRec *dc = myDirConfig(r);
++ SSLSrvConfigRec *sc = mySrvConfig(r->server);
++ SSLConnRec *sslconn = myConnConfig(r->connection);
++ SSL *ssl = sslconn ? sslconn->ssl : NULL;
++ apr_array_header_t *requires;
++ ssl_require_t *ssl_requires;
++ int ok, i, ret;
++
++ /* On a slave connection, we do not expect to have an SSLConnRec, but
++ * our master connection might have one. */
++ if (!(sslconn && ssl) && r->connection->master) {
++ sslconn = myConnConfig(r->connection->master);
++ ssl = sslconn ? sslconn->ssl : NULL;
++ }
++
++ /*
++ * We should have handshaken here, otherwise we are being
++ * redirected (ErrorDocument) from a renegotiation failure below.
++ * The access is still forbidden in the latter case, let ap_die() handle
++ * this recursive (same) error.
++ */
++ if (ssl && !SSL_is_init_finished(ssl)) {
++ return HTTP_FORBIDDEN;
++ }
++
++ /*
++ * Support for SSLRequireSSL directive
++ */
++ if (dc->bSSLRequired && !ssl) {
++ if ((sc->enabled == SSL_ENABLED_OPTIONAL) && !r->connection->master) {
++ /* This vhost was configured for optional SSL, just tell the
++ * client that we need to upgrade.
++ */
++ apr_table_setn(r->err_headers_out, "Upgrade", "TLS/1.0, HTTP/1.1");
++ apr_table_setn(r->err_headers_out, "Connection", "Upgrade");
++
++ return HTTP_UPGRADE_REQUIRED;
++ }
++
++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02219)
++ "access to %s failed, reason: %s",
++ r->filename, "SSL connection required");
++
++ /* remember forbidden access for strict require option */
++ apr_table_setn(r->notes, "ssl-access-forbidden", "1");
++
++ return HTTP_FORBIDDEN;
++ }
++
++ /*
++ * Check to see whether SSL is in use; if it's not, then no
++ * further access control checks are relevant. (the test for
++ * sc->enabled is probably strictly unnecessary)
++ */
++ if (sc->enabled == SSL_ENABLED_FALSE || !ssl) {
++ return DECLINED;
++ }
++
++#if SSL_HAVE_PROTOCOL_TLSV1_3
++ /* TLSv1.3+ is less complicated here. Branch off into a new codeline
++ * and avoid messing with the past. */
++ if (SSL_version(ssl) >= TLS1_3_VERSION) {
++ ret = ssl_hook_Access_modern(r, sc, dc, sslconn, ssl);
++ }
++ else
++#endif
++ {
++ ret = ssl_hook_Access_classic(r, sc, dc, sslconn, ssl);
++ }
++
++ if (ret != DECLINED) {
++ return ret;
++ }
++
+ /* If we're trying to have the user name set from a client
+ * certificate then we need to set it here. This should be safe as
+ * the user name probably isn't important from an auth checking point
+@@ -2080,31 +2246,43 @@
+ {
+ conn_rec *c;
+ server_rec *s;
+- SSLConnRec *scr;
+
+ /* Retrieve the conn_rec and the associated SSLConnRec. */
+ if ((c = (conn_rec *)SSL_get_app_data((SSL *)ssl)) == NULL) {
+ return;
+ }
+
+- if ((scr = myConnConfig(c)) == NULL) {
+- return;
+- }
++ /* With TLS 1.3 this callback may be called multiple times on the first
++ * negotiation, so the below logic to detect renegotiations can't work.
++ * Fortunately renegotiations are forbidden starting with TLS 1.3, and
++ * this is enforced by OpenSSL so there's nothing to be done here.
++ */
++#if SSL_HAVE_PROTOCOL_TLSV1_3
++ if (SSL_version(ssl) < TLS1_3_VERSION)
++#endif
++ {
++ SSLConnRec *sslconn;
++
++ if ((sslconn = myConnConfig(c)) == NULL) {
++ return;
++ }
+
+- /* If the reneg state is to reject renegotiations, check the SSL
+- * state machine and move to ABORT if a Client Hello is being
+- * read. */
+- if (!scr->is_proxy &&
+- (where & SSL_CB_HANDSHAKE_START) &&
+- scr->reneg_state == RENEG_REJECT) {
+- scr->reneg_state = RENEG_ABORT;
++ /* If the reneg state is to reject renegotiations, check the SSL
++ * state machine and move to ABORT if a Client Hello is being
++ * read. */
++ if (!sslconn->is_proxy &&
++ (where & SSL_CB_HANDSHAKE_START) &&
++ sslconn->reneg_state == RENEG_REJECT) {
++ sslconn->reneg_state = RENEG_ABORT;
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02042)
+ "rejecting client initiated renegotiation");
+- }
+- /* If the first handshake is complete, change state to reject any
+- * subsequent client-initiated renegotiation. */
+- else if ((where & SSL_CB_HANDSHAKE_DONE) && scr->reneg_state == RENEG_INIT) {
+- scr->reneg_state = RENEG_REJECT;
++ }
++ /* If the first handshake is complete, change state to reject any
++ * subsequent client-initiated renegotiation. */
++ else if ((where & SSL_CB_HANDSHAKE_DONE)
++ && sslconn->reneg_state == RENEG_INIT) {
++ sslconn->reneg_state = RENEG_REJECT;
++ }
+ }
+
+ s = mySrvFromConn(c);
+--- httpd-2.4.34/modules/ssl/ssl_private.h.r1827912+
++++ httpd-2.4.34/modules/ssl/ssl_private.h
+@@ -132,13 +132,14 @@
+ SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, NULL)
+ #define SSL_CTX_set_max_proto_version(ctx, version) \
+ SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, NULL)
+-#endif
+-/* LibreSSL declares OPENSSL_VERSION_NUMBER == 2.0 but does not include most
+- * changes from OpenSSL >= 1.1 (new functions, macros, deprecations, ...), so
+- * we have to work around this...
++#elif LIBRESSL_VERSION_NUMBER < 0x2070000f
++/* LibreSSL before 2.7 declares OPENSSL_VERSION_NUMBER == 2.0 but does not
++ * include most changes from OpenSSL >= 1.1 (new functions, macros,
++ * deprecations, ...), so we have to work around this...
+ */
+ #define MODSSL_USE_OPENSSL_PRE_1_1_API (1)
+-#else
++#endif /* LIBRESSL_VERSION_NUMBER < 0x2060000f */
++#else /* defined(LIBRESSL_VERSION_NUMBER) */
+ #define MODSSL_USE_OPENSSL_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
+ #endif
+
+@@ -238,7 +239,8 @@
+ void free_bio_methods(void);
+ #endif
+
+-#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
++#if OPENSSL_VERSION_NUMBER < 0x10002000L || \
++ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000f)
+ #define X509_STORE_CTX_get0_store(x) (x->ctx)
+ #endif
+
+@@ -372,8 +374,17 @@
+ #ifdef HAVE_TLSV1_X
+ #define SSL_PROTOCOL_TLSV1_1 (1<<3)
+ #define SSL_PROTOCOL_TLSV1_2 (1<<4)
++#define SSL_PROTOCOL_TLSV1_3 (1<<5)
++
++#ifdef SSL_OP_NO_TLSv1_3
++#define SSL_HAVE_PROTOCOL_TLSV1_3 (1)
++#define SSL_PROTOCOL_ALL (SSL_PROTOCOL_BASIC| \
++ SSL_PROTOCOL_TLSV1_1|SSL_PROTOCOL_TLSV1_2|SSL_PROTOCOL_TLSV1_3)
++#else
++#define SSL_HAVE_PROTOCOL_TLSV1_3 (0)
+ #define SSL_PROTOCOL_ALL (SSL_PROTOCOL_BASIC| \
+ SSL_PROTOCOL_TLSV1_1|SSL_PROTOCOL_TLSV1_2)
++#endif
+ #else
+ #define SSL_PROTOCOL_ALL (SSL_PROTOCOL_BASIC)
+ #endif
+@@ -646,6 +657,11 @@
+ /** for client or downstream server authentication */
+ int verify_depth;
+ ssl_verify_t verify_mode;
++
++ /** TLSv1.3 has its separate cipher list, separate from the
++ settings for older TLS protocol versions. Since which one takes
++ effect is a matter of negotiation, we need separate settings */
++ const char *tls13_ciphers;
+ } modssl_auth_ctx_t;
+
+ #ifdef HAVE_TLS_SESSION_TICKETS
+@@ -801,7 +817,7 @@
+ const char *ssl_cmd_SSLCryptoDevice(cmd_parms *, void *, const char *);
+ const char *ssl_cmd_SSLRandomSeed(cmd_parms *, void *, const char *, const char *, const char *);
+ const char *ssl_cmd_SSLEngine(cmd_parms *, void *, const char *);
+-const char *ssl_cmd_SSLCipherSuite(cmd_parms *, void *, const char *);
++const char *ssl_cmd_SSLCipherSuite(cmd_parms *, void *, const char *, const char *);
+ const char *ssl_cmd_SSLCertificateFile(cmd_parms *, void *, const char *);
+ const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *, void *, const char *);
+ const char *ssl_cmd_SSLCertificateChainFile(cmd_parms *, void *, const char *);
+@@ -830,7 +846,7 @@
+
+ const char *ssl_cmd_SSLProxyEngine(cmd_parms *cmd, void *dcfg, int flag);
+ const char *ssl_cmd_SSLProxyProtocol(cmd_parms *, void *, const char *);
+-const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *, void *, const char *);
++const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *, void *, const char *, const char *);
+ const char *ssl_cmd_SSLProxyVerify(cmd_parms *, void *, const char *);
+ const char *ssl_cmd_SSLProxyVerifyDepth(cmd_parms *, void *, const char *);
+ const char *ssl_cmd_SSLProxyCACertificatePath(cmd_parms *, void *, const char *);
diff --git a/httpd-2.4.34-sslciphdefault.patch b/httpd-2.4.34-sslciphdefault.patch
new file mode 100644
index 0000000..6060f24
--- /dev/null
+++ b/httpd-2.4.34-sslciphdefault.patch
@@ -0,0 +1,34 @@
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1109119
+
+Don't prepend !aNULL etc if PROFILE= is used with SSLCipherSuite.
+
+--- httpd-2.4.34/modules/ssl/ssl_engine_config.c.sslciphdefault
++++ httpd-2.4.34/modules/ssl/ssl_engine_config.c
+@@ -774,9 +774,11 @@
+ }
+
+ if (!strcmp("SSL", arg1)) {
+- /* always disable null and export ciphers */
+- arg2 = apr_pstrcat(cmd->pool, arg2, ":!aNULL:!eNULL:!EXP", NULL);
+ if (cmd->path) {
++ /* Disable null and export ciphers by default, except for PROFILE=
++ * configs where the parser doesn't cope. */
++ if (strncmp(arg2, "PROFILE=", 8) != 0)
++ arg2 = apr_pstrcat(cmd->pool, arg2, ":!aNULL:!eNULL:!EXP", NULL);
+ dc->szCipherSuite = arg2;
+ }
+ else {
+@@ -1540,8 +1542,10 @@
+ }
+
+ if (!strcmp("SSL", arg1)) {
+- /* always disable null and export ciphers */
+- arg2 = apr_pstrcat(cmd->pool, arg2, ":!aNULL:!eNULL:!EXP", NULL);
++ /* Disable null and export ciphers by default, except for PROFILE=
++ * configs where the parser doesn't cope. */
++ if (strncmp(arg2, "PROFILE=", 8) != 0)
++ arg2 = apr_pstrcat(cmd->pool, arg2, ":!aNULL:!eNULL:!EXP", NULL);
+ dc->proxy->auth.cipher_suite = arg2;
+ return NULL;
+ }
diff --git a/httpd-2.4.34-sslprotdefault.patch b/httpd-2.4.34-sslprotdefault.patch
new file mode 100644
index 0000000..65f8d40
--- /dev/null
+++ b/httpd-2.4.34-sslprotdefault.patch
@@ -0,0 +1,53 @@
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1618371
+
+--- httpd-2.4.34/modules/ssl/ssl_engine_config.c.sslprotdefault
++++ httpd-2.4.34/modules/ssl/ssl_engine_config.c
+@@ -119,7 +119,7 @@
+ mctx->ticket_key = NULL;
+ #endif
+
+- mctx->protocol = SSL_PROTOCOL_DEFAULT;
++ mctx->protocol = SSL_PROTOCOL_NONE;
+ mctx->protocol_set = 0;
+
+ mctx->pphrase_dialog_type = SSL_PPTYPE_UNSET;
+--- httpd-2.4.34/modules/ssl/ssl_engine_init.c.sslprotdefault
++++ httpd-2.4.34/modules/ssl/ssl_engine_init.c
+@@ -555,9 +555,8 @@
+ * Create the new per-server SSL context
+ */
+ if (protocol == SSL_PROTOCOL_NONE) {
+- ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02231)
+- "No SSL protocols available [hint: SSLProtocol]");
+- return ssl_die(s);
++ ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
++ "Using OpenSSL/system default SSL/TLS protocols");
+ }
+
+ cp = apr_pstrcat(p,
+@@ -673,14 +672,8 @@
+ } else if (protocol & SSL_PROTOCOL_SSLV3) {
+ prot = SSL3_VERSION;
+ #endif
+- } else {
+- SSL_CTX_free(ctx);
+- mctx->ssl_ctx = NULL;
+- ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(03378)
+- "No SSL protocols available [hint: SSLProtocol]");
+- return ssl_die(s);
+ }
+- SSL_CTX_set_max_proto_version(ctx, prot);
++ if (protocol != SSL_PROTOCOL_NONE) SSL_CTX_set_max_proto_version(ctx, prot);
+
+ /* Next we scan for the minimal protocol version we should provide,
+ * but we do not allow holes between max and min */
+@@ -700,7 +693,7 @@
+ prot = SSL3_VERSION;
+ }
+ #endif
+- SSL_CTX_set_min_proto_version(ctx, prot);
++ if (protocol != SSL_PROTOCOL_NONE) SSL_CTX_set_min_proto_version(ctx, prot);
+ #endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L */
+
+ #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
diff --git a/httpd-2.4.34.tar.bz2 b/httpd-2.4.34.tar.bz2
new file mode 100644
index 0000000..dd0f24c
Binary files /dev/null and b/httpd-2.4.34.tar.bz2 differ
diff --git a/httpd-2.4.4-cachehardmax.patch b/httpd-2.4.4-cachehardmax.patch
new file mode 100644
index 0000000..de360ce
--- /dev/null
+++ b/httpd-2.4.4-cachehardmax.patch
@@ -0,0 +1,82 @@
+diff --git a/modules/cache/cache_util.h b/modules/cache/cache_util.h
+index eec38f3..1a2d5ee 100644
+--- a/modules/cache/cache_util.h
++++ b/modules/cache/cache_util.h
+@@ -194,6 +194,9 @@ typedef struct {
+ unsigned int store_nostore_set:1;
+ unsigned int enable_set:1;
+ unsigned int disable_set:1;
++ /* treat maxex as hard limit */
++ unsigned int hardmaxex:1;
++ unsigned int hardmaxex_set:1;
+ } cache_dir_conf;
+
+ /* A linked-list of authn providers. */
+diff --git a/modules/cache/mod_cache.c b/modules/cache/mod_cache.c
+index 4f2d3e0..30c88f4 100644
+--- a/modules/cache/mod_cache.c
++++ b/modules/cache/mod_cache.c
+@@ -1299,6 +1299,11 @@ static apr_status_t cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
+ exp = date + dconf->defex;
+ }
+ }
++ /* else, forcibly cap the expiry date if required */
++ else if (dconf->hardmaxex && (date + dconf->maxex) < exp) {
++ exp = date + dconf->maxex;
++ }
++
+ info->expire = exp;
+
+ /* We found a stale entry which wasn't really stale. */
+@@ -1717,7 +1722,9 @@ static void *create_dir_config(apr_pool_t *p, char *dummy)
+
+ /* array of providers for this URL space */
+ dconf->cacheenable = apr_array_make(p, 10, sizeof(struct cache_enable));
+-
++ /* flag; treat maxex as hard limit */
++ dconf->hardmaxex = 0;
++ dconf->hardmaxex_set = 0;
+ return dconf;
+ }
+
+@@ -1767,7 +1774,10 @@ static void *merge_dir_config(apr_pool_t *p, void *basev, void *addv) {
+ new->enable_set = add->enable_set || base->enable_set;
+ new->disable = (add->disable_set == 0) ? base->disable : add->disable;
+ new->disable_set = add->disable_set || base->disable_set;
+-
++ new->hardmaxex =
++ (add->hardmaxex_set == 0)
++ ? base->hardmaxex
++ : add->hardmaxex;
+ return new;
+ }
+
+@@ -2096,12 +2106,18 @@ static const char *add_cache_disable(cmd_parms *parms, void *dummy,
+ }
+
+ static const char *set_cache_maxex(cmd_parms *parms, void *dummy,
+- const char *arg)
++ const char *arg, const char *hard)
+ {
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
+
+ dconf->maxex = (apr_time_t) (atol(arg) * MSEC_ONE_SEC);
+ dconf->maxex_set = 1;
++
++ if (hard && strcasecmp(hard, "hard") == 0) {
++ dconf->hardmaxex = 1;
++ dconf->hardmaxex_set = 1;
++ }
++
+ return NULL;
+ }
+
+@@ -2309,7 +2325,7 @@ static const command_rec cache_cmds[] =
+ "caching is enabled"),
+ AP_INIT_TAKE1("CacheDisable", add_cache_disable, NULL, RSRC_CONF|ACCESS_CONF,
+ "A partial URL prefix below which caching is disabled"),
+- AP_INIT_TAKE1("CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF|ACCESS_CONF,
++ AP_INIT_TAKE12("CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF|ACCESS_CONF,
+ "The maximum time in seconds to cache a document"),
+ AP_INIT_TAKE1("CacheMinExpire", set_cache_minex, NULL, RSRC_CONF|ACCESS_CONF,
+ "The minimum time in seconds to cache a document"),
diff --git a/httpd-2.4.4-r1337344+.patch b/httpd-2.4.4-r1337344+.patch
new file mode 100644
index 0000000..6e5c3e7
--- /dev/null
+++ b/httpd-2.4.4-r1337344+.patch
@@ -0,0 +1,250 @@
+# ./pullrev.sh 1337344 1341905 1342065 1341930
+
+suexec enhancements:
+
+1) use syslog for logging
+2) use capabilities not setuid/setgid root binary
+
+http://svn.apache.org/viewvc?view=revision&revision=1337344
+http://svn.apache.org/viewvc?view=revision&revision=1341905
+http://svn.apache.org/viewvc?view=revision&revision=1342065
+http://svn.apache.org/viewvc?view=revision&revision=1341930
+
+--- httpd-2.4.4/configure.in.r1337344+
++++ httpd-2.4.4/configure.in
+@@ -734,7 +734,24 @@ APACHE_HELP_STRING(--with-suexec-gidmin,
+
+ AC_ARG_WITH(suexec-logfile,
+ APACHE_HELP_STRING(--with-suexec-logfile,Set the logfile),[
+- AC_DEFINE_UNQUOTED(AP_LOG_EXEC, "$withval", [SuExec log file] ) ] )
++ if test "x$withval" = "xyes"; then
++ AC_DEFINE_UNQUOTED(AP_LOG_EXEC, "$withval", [SuExec log file])
++ fi
++])
++
++AC_ARG_WITH(suexec-syslog,
++APACHE_HELP_STRING(--with-suexec-syslog,Set the logfile),[
++ if test $withval = "yes"; then
++ if test "x${with_suexec_logfile}" != "xno"; then
++ AC_MSG_NOTICE([hint: use "--without-suexec-logfile --with-suexec-syslog"])
++ AC_MSG_ERROR([suexec does not support both logging to file and syslog])
++ fi
++ AC_CHECK_FUNCS([vsyslog], [], [
++ AC_MSG_ERROR([cannot support syslog from suexec without vsyslog()])])
++ AC_DEFINE(AP_LOG_SYSLOG, 1, [SuExec log to syslog])
++ fi
++])
++
+
+ AC_ARG_WITH(suexec-safepath,
+ APACHE_HELP_STRING(--with-suexec-safepath,Set the safepath),[
+@@ -744,6 +761,15 @@ AC_ARG_WITH(suexec-umask,
+ APACHE_HELP_STRING(--with-suexec-umask,umask for suexec'd process),[
+ AC_DEFINE_UNQUOTED(AP_SUEXEC_UMASK, 0$withval, [umask for suexec'd process] ) ] )
+
++INSTALL_SUEXEC=setuid
++AC_ARG_ENABLE([suexec-capabilities],
++APACHE_HELP_STRING(--enable-suexec-capabilities,Use Linux capability bits not setuid root suexec), [
++INSTALL_SUEXEC=caps
++AC_DEFINE(AP_SUEXEC_CAPABILITIES, 1,
++ [Enable if suexec is installed with Linux capabilities, not setuid])
++])
++APACHE_SUBST(INSTALL_SUEXEC)
++
+ dnl APR should go after the other libs, so the right symbols can be picked up
+ if test x${apu_found} != xobsolete; then
+ AP_LIBS="$AP_LIBS `$apu_config --avoid-ldap --link-libtool`"
+--- httpd-2.4.4/docs/manual/suexec.html.en.r1337344+
++++ httpd-2.4.4/docs/manual/suexec.html.en
+@@ -372,6 +372,21 @@
+ together with the --enable-suexec option to let
+ APACI accept your request for using the suEXEC feature.
+
++ --enable-suexec-capabilities
++
++ Linux specific: Normally,
++ the 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
+
+ The path to the suexec binary must be hard-coded
+@@ -433,6 +448,12 @@
+ "suexec_log" and located in your standard logfile
+ directory (--logfiledir).
+
++ --with-suexec-syslog
++
++ If defined, suexec will log notices and errors to syslog
++ instead of a logfile. This option must be combined
++ with --without-suexec-logfile.
++
+ --with-suexec-safepath=PATH
+
+ Define a safe PATH environment to pass to CGI
+@@ -550,9 +571,12 @@ Group webgroup
+
+ The suEXEC wrapper will write log information
+ to the file defined with the --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.
++ option as indicated above, or to syslog if --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.
+
+ 
+
+@@ -640,4 +664,4 @@ if (typeof(prettyPrint) !== 'undefined')
+ prettyPrint();
+ }
+ //-->
+-