You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2021/10/12 13:34:01 UTC

svn commit: r1894163 [1/8] - in /httpd/httpd/trunk: ./ changes-entries/ modules/http2/ test/modules/http2/

Author: icing
Date: Tue Oct 12 13:34:01 2021
New Revision: 1894163

URL: http://svn.apache.org/viewvc?rev=1894163&view=rev
Log:
  *) mod_http2:
     - Fixed an issue since 1.15.24 that "Server" headers in proxied requests
       were overwritten instead of preserved. [PR by @daum3ns]
     - Added directove 'H2StreamTimeout' to configure a separate value for HTTP/2
       streams, overriding server's 'Timeout' configuration. [rpluem]
     - HTTP/2 connections now use pollsets to monitor the status of the
       ongoing streams and their main connection when host OS allows this.
     - Removed work-arounds for older versions of libnghttp2 and checking
       during configure that at least version 1.15.0 is present.
     - The HTTP/2 connection state handler, based on an experiment and draft
       at the IETF http working group (abandoned for some time), has been removed.
     - H2SerializeHeaders no longer has an effect. A warning is logged when it is
       set to "on". The switch enabled the internal writing of requests to be parsed
       by the internal HTTP/1.1 protocol handler and was introduced to avoid
       potential incompatibilities during the introduction of HTTP/2.
     - Removed the abort/redo of tasks when mood swings lower the active limit.


Added:
    httpd/httpd/trunk/changes-entries/http2_additions.txt
    httpd/httpd/trunk/modules/http2/h2_c1.c
    httpd/httpd/trunk/modules/http2/h2_c1.h
    httpd/httpd/trunk/modules/http2/h2_c1_io.c
    httpd/httpd/trunk/modules/http2/h2_c1_io.h
    httpd/httpd/trunk/modules/http2/h2_c2.c
    httpd/httpd/trunk/modules/http2/h2_c2.h
    httpd/httpd/trunk/modules/http2/h2_c2_filter.c
    httpd/httpd/trunk/modules/http2/h2_c2_filter.h
    httpd/httpd/trunk/modules/http2/h2_conn_ctx.c
    httpd/httpd/trunk/modules/http2/h2_conn_ctx.h
    httpd/httpd/trunk/modules/http2/h2_protocol.c
    httpd/httpd/trunk/modules/http2/h2_protocol.h
Removed:
    httpd/httpd/trunk/modules/http2/h2_alt_svc.c
    httpd/httpd/trunk/modules/http2/h2_alt_svc.h
    httpd/httpd/trunk/modules/http2/h2_conn.c
    httpd/httpd/trunk/modules/http2/h2_conn.h
    httpd/httpd/trunk/modules/http2/h2_conn_io.c
    httpd/httpd/trunk/modules/http2/h2_conn_io.h
    httpd/httpd/trunk/modules/http2/h2_ctx.c
    httpd/httpd/trunk/modules/http2/h2_ctx.h
    httpd/httpd/trunk/modules/http2/h2_filter.c
    httpd/httpd/trunk/modules/http2/h2_filter.h
    httpd/httpd/trunk/modules/http2/h2_from_h1.c
    httpd/httpd/trunk/modules/http2/h2_from_h1.h
    httpd/httpd/trunk/modules/http2/h2_h2.c
    httpd/httpd/trunk/modules/http2/h2_h2.h
    httpd/httpd/trunk/modules/http2/h2_task.c
    httpd/httpd/trunk/modules/http2/h2_task.h
Modified:
    httpd/httpd/trunk/CMakeLists.txt
    httpd/httpd/trunk/modules/http2/NWGNUmod_http2
    httpd/httpd/trunk/modules/http2/config2.m4
    httpd/httpd/trunk/modules/http2/h2.h
    httpd/httpd/trunk/modules/http2/h2_bucket_beam.c
    httpd/httpd/trunk/modules/http2/h2_bucket_beam.h
    httpd/httpd/trunk/modules/http2/h2_config.c
    httpd/httpd/trunk/modules/http2/h2_config.h
    httpd/httpd/trunk/modules/http2/h2_headers.c
    httpd/httpd/trunk/modules/http2/h2_mplx.c
    httpd/httpd/trunk/modules/http2/h2_mplx.h
    httpd/httpd/trunk/modules/http2/h2_proxy_session.c
    httpd/httpd/trunk/modules/http2/h2_proxy_util.c
    httpd/httpd/trunk/modules/http2/h2_proxy_util.h
    httpd/httpd/trunk/modules/http2/h2_push.c
    httpd/httpd/trunk/modules/http2/h2_request.c
    httpd/httpd/trunk/modules/http2/h2_request.h
    httpd/httpd/trunk/modules/http2/h2_session.c
    httpd/httpd/trunk/modules/http2/h2_session.h
    httpd/httpd/trunk/modules/http2/h2_stream.c
    httpd/httpd/trunk/modules/http2/h2_stream.h
    httpd/httpd/trunk/modules/http2/h2_switch.c
    httpd/httpd/trunk/modules/http2/h2_util.c
    httpd/httpd/trunk/modules/http2/h2_util.h
    httpd/httpd/trunk/modules/http2/h2_version.h
    httpd/httpd/trunk/modules/http2/h2_workers.c
    httpd/httpd/trunk/modules/http2/h2_workers.h
    httpd/httpd/trunk/modules/http2/mod_http2.c
    httpd/httpd/trunk/modules/http2/mod_http2.dsp
    httpd/httpd/trunk/modules/http2/mod_proxy_http2.c
    httpd/httpd/trunk/test/modules/http2/test_105_timeout.py
    httpd/httpd/trunk/test/modules/http2/test_712_buffering.py

Modified: httpd/httpd/trunk/CMakeLists.txt
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CMakeLists.txt?rev=1894163&r1=1894162&r2=1894163&view=diff
==============================================================================
--- httpd/httpd/trunk/CMakeLists.txt (original)
+++ httpd/httpd/trunk/CMakeLists.txt Tue Oct 12 13:34:01 2021
@@ -469,18 +469,15 @@ SET(mod_http2_extra_defines          ssi
 SET(mod_http2_extra_includes         ${NGHTTP2_INCLUDE_DIR})
 SET(mod_http2_extra_libs             ${NGHTTP2_LIBRARIES})
 SET(mod_http2_extra_sources
-  modules/http2/h2_alt_svc.c
-  modules/http2/h2_bucket_eos.c      modules/http2/h2_config.c
-  modules/http2/h2_conn.c            modules/http2/h2_conn_io.c
-  modules/http2/h2_ctx.c             modules/http2/h2_filter.c
-  modules/http2/h2_from_h1.c         modules/http2/h2_h2.c
-  modules/http2/h2_bucket_beam.c
-  modules/http2/h2_mplx.c            modules/http2/h2_push.c
-  modules/http2/h2_request.c         modules/http2/h2_headers.c
-  modules/http2/h2_session.c         modules/http2/h2_stream.c 
-  modules/http2/h2_switch.c
-  modules/http2/h2_task.c            modules/http2/h2_util.c
-  modules/http2/h2_workers.c
+  modules/http2/h2_bucket_beam.c     modules/http2/h2_bucket_eos.c
+  modules/http2/h2_c1.c              modules/http2/h2_c1_io.c
+  modules/http2/h2_c2.c              modules/http2/h2_c2_filter.c
+  modules/http2/h2_config.c          modules/http2/h2_conn_ctx.c
+  modules/http2/h2_headers.c         modules/http2/h2_mplx.c
+  modules/http2/h2_protocol.c        modules/http2/h2_push.c
+  modules/http2/h2_request.c         modules/http2/h2_session.c
+  modules/http2/h2_stream.c          modules/http2/h2_switch.c
+  modules/http2/h2_util.c            modules/http2/h2_workers.c
 )
 SET(mod_ldap_extra_defines           LDAP_DECLARE_EXPORT)
 SET(mod_ldap_extra_libs              wldap32)

Added: httpd/httpd/trunk/changes-entries/http2_additions.txt
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/changes-entries/http2_additions.txt?rev=1894163&view=auto
==============================================================================
--- httpd/httpd/trunk/changes-entries/http2_additions.txt (added)
+++ httpd/httpd/trunk/changes-entries/http2_additions.txt Tue Oct 12 13:34:01 2021
@@ -0,0 +1,17 @@
+  *) mod_http2:
+     - Fixed an issue since 1.15.24 that "Server" headers in proxied requests
+       were overwritten instead of preserved. [PR by @daum3ns]
+     - Added directove 'H2StreamTimeout' to configure a separate value for HTTP/2
+       streams, overriding server's 'Timeout' configuration. [rpluem]
+     - HTTP/2 connections now use pollsets to monitor the status of the
+       ongoing streams and their main connection when host OS allows this.
+     - Removed work-arounds for older versions of libnghttp2 and checking
+       during configure that at least version 1.15.0 is present.
+     - The HTTP/2 connection state handler, based on an experiment and draft
+       at the IETF http working group (abandoned for some time), has been removed.
+     - H2SerializeHeaders no longer has an effect. A warning is logged when it is
+       set to "on". The switch enabled the internal writing of requests to be parsed
+       by the internal HTTP/1.1 protocol handler and was introduced to avoid
+       potential incompatibilities during the introduction of HTTP/2.
+     - Removed the abort/redo of tasks when mood swings lower the active limit.
+     [Ruediger Pluem, daum3ns, Stefan Eissing]
\ No newline at end of file

Modified: httpd/httpd/trunk/modules/http2/NWGNUmod_http2
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/NWGNUmod_http2?rev=1894163&r1=1894162&r2=1894163&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/NWGNUmod_http2 (original)
+++ httpd/httpd/trunk/modules/http2/NWGNUmod_http2 Tue Oct 12 13:34:01 2021
@@ -184,27 +184,25 @@ TARGET_lib = \
 # Paths must all use the '/' character
 #
 FILES_nlm_objs = \
-	$(OBJDIR)/h2_alt_svc.o \
-	$(OBJDIR)/h2_bucket_beam.o \
-	$(OBJDIR)/h2_bucket_eos.o \
-	$(OBJDIR)/h2_config.o \
-	$(OBJDIR)/h2_conn.o \
-	$(OBJDIR)/h2_conn_io.o \
-	$(OBJDIR)/h2_ctx.o \
-	$(OBJDIR)/h2_filter.o \
-	$(OBJDIR)/h2_from_h1.o \
-	$(OBJDIR)/h2_h2.o \
-	$(OBJDIR)/h2_mplx.o \
-	$(OBJDIR)/h2_push.o \
-	$(OBJDIR)/h2_request.o \
-	$(OBJDIR)/h2_headers.o \
-	$(OBJDIR)/h2_session.o \
-	$(OBJDIR)/h2_stream.o \
-	$(OBJDIR)/h2_switch.o \
-	$(OBJDIR)/h2_task.o \
-	$(OBJDIR)/h2_util.o \
-	$(OBJDIR)/h2_workers.o \
-	$(OBJDIR)/mod_http2.o \
+	$(OBJDIR)/h2_bucket_beam.lo \
+	$(OBJDIR)/h2_bucket_eos.lo \
+	$(OBJDIR)/h2_c1.lo \
+	$(OBJDIR)/h2_c1_io.lo \
+	$(OBJDIR)/h2_c2.lo \
+	$(OBJDIR)/h2_c2_filter.lo \
+	$(OBJDIR)/h2_config.lo \
+	$(OBJDIR)/h2_conn_ctx.lo \
+	$(OBJDIR)/h2_headers.lo \
+	$(OBJDIR)/h2_mplx.lo \
+	$(OBJDIR)/h2_protocol.lo \
+	$(OBJDIR)/h2_push.lo \
+	$(OBJDIR)/h2_request.lo \
+	$(OBJDIR)/h2_session.lo \
+	$(OBJDIR)/h2_stream.lo \
+	$(OBJDIR)/h2_switch.lo \
+	$(OBJDIR)/h2_util.lo \
+	$(OBJDIR)/h2_workers.lo \
+	$(OBJDIR)/mod_http2.lo \
 	$(EOLIST)
 
 #

Modified: httpd/httpd/trunk/modules/http2/config2.m4
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/config2.m4?rev=1894163&r1=1894162&r2=1894163&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/config2.m4 (original)
+++ httpd/httpd/trunk/modules/http2/config2.m4 Tue Oct 12 13:34:01 2021
@@ -19,24 +19,22 @@ APACHE_MODPATH_INIT(http2)
 dnl #  list of module object files
 http2_objs="dnl
 mod_http2.lo dnl
-h2_alt_svc.lo dnl
 h2_bucket_beam.lo dnl
 h2_bucket_eos.lo dnl
+h2_c1.lo dnl
+h2_c1_io.lo dnl
+h2_c2.lo dnl
+h2_c2_filter.lo dnl
 h2_config.lo dnl
-h2_conn.lo dnl
-h2_conn_io.lo dnl
-h2_ctx.lo dnl
-h2_filter.lo dnl
-h2_from_h1.lo dnl
-h2_h2.lo dnl
+h2_conn_ctx.lo dnl
 h2_headers.lo dnl
 h2_mplx.lo dnl
+h2_protocol.lo dnl
 h2_push.lo dnl
 h2_request.lo dnl
 h2_session.lo dnl
 h2_stream.lo dnl
 h2_switch.lo dnl
-h2_task.lo dnl
 h2_util.lo dnl
 h2_workers.lo dnl
 "

Modified: httpd/httpd/trunk/modules/http2/h2.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2.h?rev=1894163&r1=1894162&r2=1894163&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2.h (original)
+++ httpd/httpd/trunk/modules/http2/h2.h Tue Oct 12 13:34:01 2021
@@ -17,6 +17,19 @@
 #ifndef __mod_h2__h2__
 #define __mod_h2__h2__
 
+struct h2_session;
+struct h2_stream;
+
+/*
+ * When apr pollsets can poll file descriptors (e.g. pipes),
+ * we use it for polling stream input/output.
+ */
+#ifdef H2_NO_POLL_STREAMS
+#define H2_POLL_STREAMS           0
+#else
+#define H2_POLL_STREAMS           APR_FILES_AS_SOCKETS
+#endif
+
 /**
  * The magic PRIamble of RFC 7540 that is always sent when starting
  * a h2 communication.
@@ -89,7 +102,7 @@ typedef enum {
     H2_SESSION_ST_DONE,             /* finished, connection close */
     H2_SESSION_ST_IDLE,             /* nothing to write, expecting data inc */
     H2_SESSION_ST_BUSY,             /* read/write without stop */
-    H2_SESSION_ST_WAIT,             /* waiting for tasks reporting back */
+    H2_SESSION_ST_WAIT,             /* waiting for c1 incoming + c2s output */
     H2_SESSION_ST_CLEANUP,          /* pool is being cleaned up */
 } h2_session_state;
 
@@ -120,7 +133,9 @@ typedef enum {
     H2_SEV_CLOSED_R,
     H2_SEV_CANCELLED,
     H2_SEV_EOS_SENT,
+    H2_SEV_IN_ERROR,
     H2_SEV_IN_DATA_PENDING,
+    H2_SEV_OUT_C1_BLOCK,
 } h2_stream_event_t;
 
 
@@ -129,7 +144,6 @@ typedef enum {
  * become a request_rec to be handled by soemone.
  */
 typedef struct h2_request h2_request;
-
 struct h2_request {
     const char *method; /* pseudo header values, see ch. 8.1.2.3 */
     const char *scheme;
@@ -138,8 +152,7 @@ struct h2_request {
     apr_table_t *headers;
 
     apr_time_t request_time;
-    unsigned int chunked : 1;   /* iff request body needs to be forwarded as chunked */
-    unsigned int serialize : 1; /* iff this request is written in HTTP/1.1 serialization */
+    int chunked;                /* iff request body needs to be forwarded as chunked */
     apr_off_t raw_bytes;        /* RAW network bytes that generated this request - if known. */
     int http_status;            /* Store a possible HTTP status code that gets
                                  * defined before creating the dummy HTTP/1.1
@@ -155,7 +168,6 @@ struct h2_request {
 #define H2_HTTP_STATUS_UNSET (0)
 
 typedef struct h2_headers h2_headers;
-
 struct h2_headers {
     int         status;
     apr_table_t *headers;
@@ -165,12 +177,10 @@ struct h2_headers {
 
 typedef apr_status_t h2_io_data_cb(void *ctx, const char *data, apr_off_t len);
 
-typedef int h2_stream_pri_cmp(int stream_id1, int stream_id2, void *ctx);
-
-/* Note key to attach connection task id to conn_rec/request_rec instances */
+typedef int h2_stream_pri_cmp_fn(int stream_id1, int stream_id2, void *session);
+typedef struct h2_stream *h2_stream_get_fn(struct h2_session *session, int stream_id);
 
-#define H2_TASK_ID_NOTE         "http2-task-id"
-#define H2_FILTER_DEBUG_NOTE    "http2-debug"
+/* Note key to attach stream id to conn_rec/request_rec instances */
 #define H2_HDR_CONFORMANCE      "http2-hdr-conformance"
 #define H2_HDR_CONFORMANCE_UNSAFE      "unsafe"
 #define H2_PUSH_MODE_NOTE       "http2-push-mode"

Modified: httpd/httpd/trunk/modules/http2/h2_bucket_beam.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_bucket_beam.c?rev=1894163&r1=1894162&r2=1894163&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_bucket_beam.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_bucket_beam.c Tue Oct 12 13:34:01 2021
@@ -27,133 +27,33 @@
 #include <http_log.h>
 
 #include "h2_private.h"
+#include "h2_conn_ctx.h"
 #include "h2_util.h"
 #include "h2_bucket_beam.h"
 
-static void h2_beam_emitted(h2_bucket_beam *beam, h2_beam_proxy *proxy);
 
-#define H2_BPROXY_NEXT(e)             APR_RING_NEXT((e), link)
-#define H2_BPROXY_PREV(e)             APR_RING_PREV((e), link)
-#define H2_BPROXY_REMOVE(e)           APR_RING_REMOVE((e), link)
-
-#define H2_BPROXY_LIST_INIT(b)        APR_RING_INIT(&(b)->list, h2_beam_proxy, link);
-#define H2_BPROXY_LIST_SENTINEL(b)    APR_RING_SENTINEL(&(b)->list, h2_beam_proxy, link)
-#define H2_BPROXY_LIST_EMPTY(b)       APR_RING_EMPTY(&(b)->list, h2_beam_proxy, link)
-#define H2_BPROXY_LIST_FIRST(b)       APR_RING_FIRST(&(b)->list)
-#define H2_BPROXY_LIST_LAST(b)	      APR_RING_LAST(&(b)->list)
-#define H2_PROXY_BLIST_INSERT_HEAD(b, e) do {				\
-	h2_beam_proxy *ap__b = (e);                                        \
-	APR_RING_INSERT_HEAD(&(b)->list, ap__b, h2_beam_proxy, link);	\
+#define H2_BLIST_INIT(b)        APR_RING_INIT(&(b)->list, apr_bucket, link);
+#define H2_BLIST_SENTINEL(b)    APR_RING_SENTINEL(&(b)->list, apr_bucket, link)
+#define H2_BLIST_EMPTY(b)       APR_RING_EMPTY(&(b)->list, apr_bucket, link)
+#define H2_BLIST_FIRST(b)       APR_RING_FIRST(&(b)->list)
+#define H2_BLIST_LAST(b)	APR_RING_LAST(&(b)->list)
+#define H2_BLIST_INSERT_HEAD(b, e) do {				\
+	apr_bucket *ap__b = (e);                                        \
+	APR_RING_INSERT_HEAD(&(b)->list, ap__b, apr_bucket, link);	\
     } while (0)
-#define H2_BPROXY_LIST_INSERT_TAIL(b, e) do {				\
-	h2_beam_proxy *ap__b = (e);					\
-	APR_RING_INSERT_TAIL(&(b)->list, ap__b, h2_beam_proxy, link);	\
+#define H2_BLIST_INSERT_TAIL(b, e) do {				\
+	apr_bucket *ap__b = (e);					\
+	APR_RING_INSERT_TAIL(&(b)->list, ap__b, apr_bucket, link);	\
     } while (0)
-#define H2_BPROXY_LIST_CONCAT(a, b) do {					\
-        APR_RING_CONCAT(&(a)->list, &(b)->list, h2_beam_proxy, link);	\
+#define H2_BLIST_CONCAT(a, b) do {					\
+        APR_RING_CONCAT(&(a)->list, &(b)->list, apr_bucket, link);	\
     } while (0)
-#define H2_BPROXY_LIST_PREPEND(a, b) do {					\
-        APR_RING_PREPEND(&(a)->list, &(b)->list, h2_beam_proxy, link);	\
+#define H2_BLIST_PREPEND(a, b) do {					\
+        APR_RING_PREPEND(&(a)->list, &(b)->list, apr_bucket, link);	\
     } while (0)
 
 
-/*******************************************************************************
- * beam bucket with reference to beam and bucket it represents
- ******************************************************************************/
-
-const apr_bucket_type_t h2_bucket_type_beam;
-
-#define H2_BUCKET_IS_BEAM(e)     (e->type == &h2_bucket_type_beam)
-
-struct h2_beam_proxy {
-    apr_bucket_refcount refcount;
-    APR_RING_ENTRY(h2_beam_proxy) link;
-    h2_bucket_beam *beam;
-    apr_bucket *bsender;
-    apr_size_t n;
-};
-
-static const char Dummy = '\0';
-
-static apr_status_t beam_bucket_read(apr_bucket *b, const char **str, 
-                                     apr_size_t *len, apr_read_type_e block)
-{
-    h2_beam_proxy *d = b->data;
-    if (d->bsender) {
-        const char *data;
-        apr_status_t status = apr_bucket_read(d->bsender, &data, len, block);
-        if (status == APR_SUCCESS) {
-            *str = data + b->start;
-            *len = b->length;
-        }
-        return status;
-    }
-    *str = &Dummy;
-    *len = 0;
-    return APR_ECONNRESET;
-}
-
-static void beam_bucket_destroy(void *data)
-{
-    h2_beam_proxy *d = data;
-
-    if (apr_bucket_shared_destroy(d)) {
-        /* When the beam gets destroyed before this bucket, it will
-         * NULLify its reference here. This is not protected by a mutex,
-         * so it will not help with race conditions.
-         * But it lets us shut down memory pool with circulare beam
-         * references. */
-        if (d->beam) {
-            h2_beam_emitted(d->beam, d);
-        }
-        apr_bucket_free(d);
-    }
-}
-
-static apr_bucket * h2_beam_bucket_make(apr_bucket *b, 
-                                        h2_bucket_beam *beam,
-                                        apr_bucket *bsender, apr_size_t n)
-{
-    h2_beam_proxy *d;
-
-    d = apr_bucket_alloc(sizeof(*d), b->list);
-    H2_BPROXY_LIST_INSERT_TAIL(&beam->proxies, d);
-    d->beam = beam;
-    d->bsender = bsender;
-    d->n = n;
-    
-    b = apr_bucket_shared_make(b, d, 0, bsender? bsender->length : 0);
-    b->type = &h2_bucket_type_beam;
-
-    return b;
-}
-
-static apr_bucket *h2_beam_bucket_create(h2_bucket_beam *beam,
-                                         apr_bucket *bsender,
-                                         apr_bucket_alloc_t *list,
-                                         apr_size_t n)
-{
-    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
-
-    APR_BUCKET_INIT(b);
-    b->free = apr_bucket_free;
-    b->list = list;
-    return h2_beam_bucket_make(b, beam, bsender, n);
-}
-
-const apr_bucket_type_t h2_bucket_type_beam = {
-    "BEAM", 5, APR_BUCKET_DATA,
-    beam_bucket_destroy,
-    beam_bucket_read,
-    apr_bucket_setaside_noop,
-    apr_bucket_shared_split,
-    apr_bucket_shared_copy
-};
-
-/*******************************************************************************
- * h2_blist, a brigade without allocations
- ******************************************************************************/
-
+/* registry for bucket converting `h2_bucket_beamer` functions */
 static apr_array_header_t *beamers;
 
 static apr_status_t cleanup_beamers(void *dummy)
@@ -191,40 +91,53 @@ static apr_bucket *h2_beam_bucket(h2_buc
     return b;
 }
 
+static int is_empty(h2_bucket_beam *beam);
+static apr_off_t get_buffered_data_len(h2_bucket_beam *beam);
 
-/*******************************************************************************
- * bucket beam that can transport buckets across threads
- ******************************************************************************/
-
-static void mutex_leave(apr_thread_mutex_t *lock)
+static int h2_blist_count(h2_blist *blist)
 {
-    apr_thread_mutex_unlock(lock);
-}
+    apr_bucket *b;
+    int count = 0;
 
-static apr_status_t mutex_enter(void *ctx, h2_beam_lock *pbl)
-{
-    h2_bucket_beam *beam = ctx;
-    pbl->mutex = beam->lock;
-    pbl->leave = mutex_leave;
-    return apr_thread_mutex_lock(pbl->mutex);
+    for (b = H2_BLIST_FIRST(blist); b != H2_BLIST_SENTINEL(blist);
+         b = APR_BUCKET_NEXT(b)) {
+        ++count;
+    }
+    return count;
 }
 
-static apr_status_t enter_yellow(h2_bucket_beam *beam, h2_beam_lock *pbl)
-{
-    return mutex_enter(beam, pbl);
-}
+#define H2_BEAM_LOG(beam, c, level, rv, msg, bb) \
+    do { \
+        if (APLOG_C_IS_LEVEL((c),(level))) { \
+            char buffer[4 * 1024]; \
+            apr_size_t len, bmax = sizeof(buffer)/sizeof(buffer[0]); \
+            len = bb? h2_util_bb_print(buffer, bmax, "", "", bb) : 0; \
+            ap_log_cerror(APLOG_MARK, (level), rv, (c), \
+                          "BEAM[%s,%s%sdata=%ld,buckets(send/consumed)=%d/%d]: %s %s", \
+                          (beam)->name, \
+                          (beam)->aborted? "aborted," : "", \
+                          is_empty(beam)? "empty," : "", \
+                          (long)get_buffered_data_len(beam), \
+                          h2_blist_count(&(beam)->buckets_to_send), \
+                          h2_blist_count(&(beam)->buckets_consumed), \
+                          (msg), len? buffer : ""); \
+        } \
+    } while (0)
+
 
-static void leave_yellow(h2_bucket_beam *beam, h2_beam_lock *pbl)
+static int bucket_is_mmap(apr_bucket *b)
 {
-    (void)beam;
-    if (pbl->leave) {
-        pbl->leave(pbl->mutex);
-    }
+#if APR_HAS_MMAP
+    return APR_BUCKET_IS_MMAP(b);
+#else
+    /* if it is not defined as enabled, it should always be no */
+    return 0;
+#endif
 }
 
 static apr_off_t bucket_mem_used(apr_bucket *b)
 {
-    if (APR_BUCKET_IS_FILE(b)) {
+    if (APR_BUCKET_IS_FILE(b) || bucket_is_mmap(b)) {
         return 0;
     }
     else {
@@ -233,53 +146,37 @@ static apr_off_t bucket_mem_used(apr_buc
     }
 }
 
-static int report_consumption(h2_bucket_beam *beam, h2_beam_lock *pbl)
+static int report_consumption(h2_bucket_beam *beam, int locked)
 {
     int rv = 0;
-    apr_off_t len = beam->received_bytes - beam->cons_bytes_reported;
+    apr_off_t len = beam->recv_bytes - beam->recv_bytes_reported;
     h2_beam_io_callback *cb = beam->cons_io_cb;
      
     if (len > 0) {
         if (cb) {
             void *ctx = beam->cons_ctx;
             
-            if (pbl) leave_yellow(beam, pbl);
+            if (locked) apr_thread_mutex_unlock(beam->lock);
             cb(ctx, beam, len);
-            if (pbl) enter_yellow(beam, pbl);
+            if (locked) apr_thread_mutex_lock(beam->lock);
             rv = 1;
         }
-        beam->cons_bytes_reported += len;
+        beam->recv_bytes_reported += len;
     }
     return rv;
 }
 
-static void report_prod_io(h2_bucket_beam *beam, int force, h2_beam_lock *pbl)
-{
-    apr_off_t len = beam->sent_bytes - beam->prod_bytes_reported;
-    if (force || len > 0) {
-        h2_beam_io_callback *cb = beam->prod_io_cb; 
-        if (cb) {
-            void *ctx = beam->prod_ctx;
-            
-            leave_yellow(beam, pbl);
-            cb(ctx, beam, len);
-            enter_yellow(beam, pbl);
-        }
-        beam->prod_bytes_reported += len;
-    }
-}
-
 static apr_size_t calc_buffered(h2_bucket_beam *beam)
 {
     apr_size_t len = 0;
     apr_bucket *b;
-    for (b = H2_BLIST_FIRST(&beam->send_list); 
-         b != H2_BLIST_SENTINEL(&beam->send_list);
+    for (b = H2_BLIST_FIRST(&beam->buckets_to_send);
+         b != H2_BLIST_SENTINEL(&beam->buckets_to_send);
          b = APR_BUCKET_NEXT(b)) {
         if (b->length == ((apr_size_t)-1)) {
             /* do not count */
         }
-        else if (APR_BUCKET_IS_FILE(b)) {
+        else if (APR_BUCKET_IS_FILE(b) || bucket_is_mmap(b)) {
             /* if unread, has no real mem footprint. */
         }
         else {
@@ -289,13 +186,13 @@ static apr_size_t calc_buffered(h2_bucke
     return len;
 }
 
-static void r_purge_sent(h2_bucket_beam *beam)
+static void purge_consumed_buckets(h2_bucket_beam *beam)
 {
     apr_bucket *b;
     /* delete all sender buckets in purge brigade, needs to be called
      * from sender thread only */
-    while (!H2_BLIST_EMPTY(&beam->purge_list)) {
-        b = H2_BLIST_FIRST(&beam->purge_list);
+    while (!H2_BLIST_EMPTY(&beam->buckets_consumed)) {
+        b = H2_BLIST_FIRST(&beam->buckets_consumed);
         apr_bucket_delete(b);
     }
 }
@@ -312,30 +209,10 @@ static apr_size_t calc_space_left(h2_buc
 static int buffer_is_empty(h2_bucket_beam *beam)
 {
     return ((!beam->recv_buffer || APR_BRIGADE_EMPTY(beam->recv_buffer))
-            && H2_BLIST_EMPTY(&beam->send_list));
+            && H2_BLIST_EMPTY(&beam->buckets_to_send));
 }
 
-static apr_status_t wait_empty(h2_bucket_beam *beam, apr_read_type_e block,  
-                               apr_thread_mutex_t *lock)
-{
-    apr_status_t rv = APR_SUCCESS;
-    
-    while (!buffer_is_empty(beam) && APR_SUCCESS == rv) {
-        if (APR_BLOCK_READ != block || !lock) {
-            rv = APR_EAGAIN;
-        }
-        else if (beam->timeout > 0) {
-            rv = apr_thread_cond_timedwait(beam->change, lock, beam->timeout);
-        }
-        else {
-            rv = apr_thread_cond_wait(beam->change, lock);
-        }
-    }
-    return rv;
-}
-
-static apr_status_t wait_not_empty(h2_bucket_beam *beam, apr_read_type_e block,  
-                                   apr_thread_mutex_t *lock)
+static apr_status_t wait_not_empty(h2_bucket_beam *beam, conn_rec *c, apr_read_type_e block)
 {
     apr_status_t rv = APR_SUCCESS;
     
@@ -343,24 +220,24 @@ static apr_status_t wait_not_empty(h2_bu
         if (beam->aborted) {
             rv = APR_ECONNABORTED;
         }
-        else if (beam->closed) {
-            rv = APR_EOF;
-        }
-        else if (APR_BLOCK_READ != block || !lock) {
+        else if (APR_BLOCK_READ != block) {
             rv = APR_EAGAIN;
         }
         else if (beam->timeout > 0) {
-            rv = apr_thread_cond_timedwait(beam->change, lock, beam->timeout);
+            H2_BEAM_LOG(beam, c, APLOG_TRACE2, rv, "wait_not_empty, timeout", NULL);
+            rv = apr_thread_cond_timedwait(beam->change, beam->lock, beam->timeout);
         }
         else {
-            rv = apr_thread_cond_wait(beam->change, lock);
+            H2_BEAM_LOG(beam, c, APLOG_TRACE2, rv, "wait_not_empty, forever", NULL);
+            rv = apr_thread_cond_wait(beam->change, beam->lock);
         }
     }
     return rv;
 }
 
-static apr_status_t wait_not_full(h2_bucket_beam *beam, apr_read_type_e block, 
-                                  apr_size_t *pspace_left, h2_beam_lock *bl)
+static apr_status_t wait_not_full(h2_bucket_beam *beam, conn_rec *c,
+                                  apr_read_type_e block,
+                                  apr_size_t *pspace_left)
 {
     apr_status_t rv = APR_SUCCESS;
     apr_size_t left;
@@ -369,15 +246,17 @@ static apr_status_t wait_not_full(h2_buc
         if (beam->aborted) {
             rv = APR_ECONNABORTED;
         }
-        else if (block != APR_BLOCK_READ || !bl->mutex) {
+        else if (block != APR_BLOCK_READ) {
             rv = APR_EAGAIN;
         }
         else {
             if (beam->timeout > 0) {
-                rv = apr_thread_cond_timedwait(beam->change, bl->mutex, beam->timeout);
+                H2_BEAM_LOG(beam, c, APLOG_TRACE2, rv, "wait_not_full, timeout", NULL);
+                rv = apr_thread_cond_timedwait(beam->change, beam->lock, beam->timeout);
             }
             else {
-                rv = apr_thread_cond_wait(beam->change, bl->mutex);
+                H2_BEAM_LOG(beam, c, APLOG_TRACE2, rv, "wait_not_full, forever", NULL);
+                rv = apr_thread_cond_wait(beam->change, beam->lock);
             }
         }
     }
@@ -385,73 +264,6 @@ static apr_status_t wait_not_full(h2_buc
     return rv;
 }
 
-static void h2_beam_emitted(h2_bucket_beam *beam, h2_beam_proxy *proxy)
-{
-    h2_beam_lock bl;
-    apr_bucket *b, *next;
-
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        /* even when beam buckets are split, only the one where
-         * refcount drops to 0 will call us */
-        H2_BPROXY_REMOVE(proxy);
-        /* invoked from receiver thread, the last beam bucket for the send
-         * bucket is about to be destroyed.
-         * remove it from the hold, where it should be now */
-        if (proxy->bsender) {
-            for (b = H2_BLIST_FIRST(&beam->hold_list); 
-                 b != H2_BLIST_SENTINEL(&beam->hold_list);
-                 b = APR_BUCKET_NEXT(b)) {
-                 if (b == proxy->bsender) {
-                    break;
-                 }
-            }
-            if (b != H2_BLIST_SENTINEL(&beam->hold_list)) {
-                /* bucket is in hold as it should be, mark this one
-                 * and all before it for purging. We might have placed meta
-                 * buckets without a receiver proxy into the hold before it 
-                 * and schedule them for purging now */
-                for (b = H2_BLIST_FIRST(&beam->hold_list); 
-                     b != H2_BLIST_SENTINEL(&beam->hold_list);
-                     b = next) {
-                    next = APR_BUCKET_NEXT(b);
-                    if (b == proxy->bsender) {
-                        APR_BUCKET_REMOVE(b);
-                        H2_BLIST_INSERT_TAIL(&beam->purge_list, b);
-                        break;
-                    }
-                    else if (APR_BUCKET_IS_METADATA(b)) {
-                        APR_BUCKET_REMOVE(b);
-                        H2_BLIST_INSERT_TAIL(&beam->purge_list, b);
-                    }
-                    else {
-                        /* another data bucket before this one in hold. this
-                         * is normal since DATA buckets need not be destroyed
-                         * in order */
-                    }
-                }
-                
-                proxy->bsender = NULL;
-            }
-            else {
-                /* it should be there unless we screwed up */
-                ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, beam->send_pool, 
-                              APLOGNO(03384) "h2_beam(%d-%s): emitted bucket not "
-                              "in hold, n=%d", beam->id, beam->tag, 
-                              (int)proxy->n);
-                ap_assert(!proxy->bsender);
-            }
-        }
-        /* notify anyone waiting on space to become available */
-        if (!bl.mutex) {
-            r_purge_sent(beam);
-        }
-        else {
-            apr_thread_cond_broadcast(beam->change);
-        }
-        leave_yellow(beam, &bl);
-    }
-}
-
 static void h2_blist_cleanup(h2_blist *bl)
 {
     apr_bucket *e;
@@ -462,80 +274,7 @@ static void h2_blist_cleanup(h2_blist *b
     }
 }
 
-static apr_status_t beam_close(h2_bucket_beam *beam)
-{
-    if (!beam->closed) {
-        beam->closed = 1;
-        apr_thread_cond_broadcast(beam->change);
-    }
-    return APR_SUCCESS;
-}
-
-int h2_beam_is_closed(h2_bucket_beam *beam)
-{
-    return beam->closed;
-}
-
-static int pool_register(h2_bucket_beam *beam, apr_pool_t *pool, 
-                         apr_status_t (*cleanup)(void *))
-{
-    if (pool && pool != beam->pool) {
-        apr_pool_pre_cleanup_register(pool, beam, cleanup);
-        return 1;
-    }
-    return 0;
-}
-
-static int pool_kill(h2_bucket_beam *beam, apr_pool_t *pool,
-                     apr_status_t (*cleanup)(void *)) {
-    if (pool && pool != beam->pool) {
-        apr_pool_cleanup_kill(pool, beam, cleanup);
-        return 1;
-    }
-    return 0;
-}
-
-static apr_status_t beam_recv_cleanup(void *data)
-{
-    h2_bucket_beam *beam = data;
-    /* receiver pool has gone away, clear references */
-    beam->recv_buffer = NULL;
-    beam->recv_pool = NULL;
-    return APR_SUCCESS;
-}
-
-static apr_status_t beam_send_cleanup(void *data)
-{
-    h2_bucket_beam *beam = data;
-    /* sender is going away, clear up all references to its memory */
-    r_purge_sent(beam);
-    h2_blist_cleanup(&beam->send_list);
-    report_consumption(beam, NULL);
-    while (!H2_BPROXY_LIST_EMPTY(&beam->proxies)) {
-        h2_beam_proxy *proxy = H2_BPROXY_LIST_FIRST(&beam->proxies);
-        H2_BPROXY_REMOVE(proxy);
-        proxy->beam = NULL;
-        proxy->bsender = NULL;
-    }
-    h2_blist_cleanup(&beam->purge_list);
-    h2_blist_cleanup(&beam->hold_list);
-    beam->send_pool = NULL;
-    return APR_SUCCESS;
-}
-
-static void beam_set_send_pool(h2_bucket_beam *beam, apr_pool_t *pool) 
-{
-    if (beam->send_pool != pool) {
-        if (beam->send_pool && beam->send_pool != beam->pool) {
-            pool_kill(beam, beam->send_pool, beam_send_cleanup);
-            beam_send_cleanup(beam);
-        }
-        beam->send_pool = pool;
-        pool_register(beam, beam->send_pool, beam_send_cleanup);
-    }
-}
-
-static void recv_buffer_cleanup(h2_bucket_beam *beam, h2_beam_lock *bl)
+static void recv_buffer_cleanup(h2_bucket_beam *beam)
 {
     if (beam->recv_buffer && !APR_BRIGADE_EMPTY(beam->recv_buffer)) {
         apr_bucket_brigade *bb = beam->recv_buffer;
@@ -543,73 +282,30 @@ static void recv_buffer_cleanup(h2_bucke
         
         beam->recv_buffer = NULL;
         apr_brigade_length(bb, 0, &bblen);
-        beam->received_bytes += bblen;
+        beam->recv_bytes += bblen;
         
         /* need to do this unlocked since bucket destroy might 
          * call this beam again. */
-        if (bl) leave_yellow(beam, bl);
+        apr_thread_mutex_unlock(beam->lock);
         apr_brigade_destroy(bb);
-        if (bl) enter_yellow(beam, bl);
-        
+        apr_thread_mutex_lock(beam->lock);
+
         apr_thread_cond_broadcast(beam->change);
-        if (beam->cons_ev_cb) { 
-            beam->cons_ev_cb(beam->cons_ctx, beam);
+        if (beam->recv_cb) {
+            beam->recv_cb(beam->recv_ctx, beam);
         }
     }
 }
 
 static apr_status_t beam_cleanup(h2_bucket_beam *beam, int from_pool)
 {
-    apr_status_t status = APR_SUCCESS;
-    int safe_send = (beam->owner == H2_BEAM_OWNER_SEND);
-    int safe_recv = (beam->owner == H2_BEAM_OWNER_RECV);
-    
-    /* 
-     * Owner of the beam is going away, depending on which side it owns,
-     * cleanup strategies will differ.
-     *
-     * In general, receiver holds references to memory from sender. 
-     * Clean up receiver first, if safe, then cleanup sender, if safe.
-     */
-     
-     /* When called from pool destroy, io callbacks are disabled */
-     if (from_pool) {
-         beam->cons_io_cb = NULL;
-     }
-     
-    /* When modify send is not safe, this means we still have multi-thread
-     * protection and the owner is receiving the buckets. If the sending
-     * side has not gone away, this means we could have dangling buckets
-     * in our lists that never get destroyed. This should not happen. */
-    ap_assert(safe_send || !beam->send_pool);
-    if (!H2_BLIST_EMPTY(&beam->send_list)) {
-        ap_assert(beam->send_pool);
-    }
-    
-    if (safe_recv) {
-        if (beam->recv_pool) {
-            pool_kill(beam, beam->recv_pool, beam_recv_cleanup);
-            beam->recv_pool = NULL;
-        }
-        recv_buffer_cleanup(beam, NULL);
-    }
-    else {
-        beam->recv_buffer = NULL;
-        beam->recv_pool = NULL;
-    }
-    
-    if (safe_send && beam->send_pool) {
-        pool_kill(beam, beam->send_pool, beam_send_cleanup);
-        status = beam_send_cleanup(beam);
-    }
-    
-    if (safe_recv) {
-        ap_assert(H2_BPROXY_LIST_EMPTY(&beam->proxies));
-        ap_assert(H2_BLIST_EMPTY(&beam->send_list));
-        ap_assert(H2_BLIST_EMPTY(&beam->hold_list));
-        ap_assert(H2_BLIST_EMPTY(&beam->purge_list));
-    }
-    return status;
+    beam->cons_io_cb = NULL;
+    beam->recv_cb = NULL;
+
+    h2_blist_cleanup(&beam->buckets_to_send);
+    recv_buffer_cleanup(beam);
+    purge_consumed_buckets(beam);
+    return APR_SUCCESS;
 }
 
 static apr_status_t beam_pool_cleanup(void *data)
@@ -617,179 +313,131 @@ static apr_status_t beam_pool_cleanup(vo
     return beam_cleanup(data, 1);
 }
 
-apr_status_t h2_beam_destroy(h2_bucket_beam *beam)
+apr_status_t h2_beam_destroy(h2_bucket_beam *beam, conn_rec *c)
 {
     apr_pool_cleanup_kill(beam->pool, beam, beam_pool_cleanup);
+    H2_BEAM_LOG(beam, c, APLOG_TRACE2, 0, "destroy", NULL);
     return beam_cleanup(beam, 0);
 }
 
-apr_status_t h2_beam_create(h2_bucket_beam **pbeam, apr_pool_t *pool, 
-                            int id, const char *tag, 
-                            h2_beam_owner_t owner,
+apr_status_t h2_beam_create(h2_bucket_beam **pbeam, conn_rec *from,
+                            apr_pool_t *pool, int id, const char *tag,
                             apr_size_t max_buf_size,
                             apr_interval_time_t timeout)
 {
     h2_bucket_beam *beam;
-    apr_status_t rv = APR_SUCCESS;
+    h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(from);
+    apr_status_t rv;
     
     beam = apr_pcalloc(pool, sizeof(*beam));
-    if (!beam) {
-        return APR_ENOMEM;
-    }
-
-    beam->id = id;
-    beam->tag = tag;
     beam->pool = pool;
-    beam->owner = owner;
-    H2_BLIST_INIT(&beam->send_list);
-    H2_BLIST_INIT(&beam->hold_list);
-    H2_BLIST_INIT(&beam->purge_list);
-    H2_BPROXY_LIST_INIT(&beam->proxies);
+    beam->from = from;
+    beam->id = id;
+    beam->name = apr_psprintf(pool, "%s-%d-%s",
+                              conn_ctx->id, id, tag);
+
+    H2_BLIST_INIT(&beam->buckets_to_send);
+    H2_BLIST_INIT(&beam->buckets_consumed);
     beam->tx_mem_limits = 1;
     beam->max_buf_size = max_buf_size;
     beam->timeout = timeout;
 
     rv = apr_thread_mutex_create(&beam->lock, APR_THREAD_MUTEX_DEFAULT, pool);
-    if (APR_SUCCESS == rv) {
-        rv = apr_thread_cond_create(&beam->change, pool);
-        if (APR_SUCCESS == rv) {
-            apr_pool_pre_cleanup_register(pool, beam, beam_pool_cleanup);
-            *pbeam = beam;
-        }
-    }
+    if (APR_SUCCESS != rv) goto cleanup;
+    rv = apr_thread_cond_create(&beam->change, pool);
+    if (APR_SUCCESS != rv) goto cleanup;
+    apr_pool_pre_cleanup_register(pool, beam, beam_pool_cleanup);
+
+cleanup:
+    H2_BEAM_LOG(beam, from, APLOG_TRACE2, rv, "created", NULL);
+    *pbeam = (APR_SUCCESS == rv)? beam : NULL;
     return rv;
 }
 
 void h2_beam_buffer_size_set(h2_bucket_beam *beam, apr_size_t buffer_size)
 {
-    h2_beam_lock bl;
-    
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        beam->max_buf_size = buffer_size;
-        leave_yellow(beam, &bl);
-    }
+    apr_thread_mutex_lock(beam->lock);
+    beam->max_buf_size = buffer_size;
+    apr_thread_mutex_unlock(beam->lock);
 }
 
-apr_size_t h2_beam_buffer_size_get(h2_bucket_beam *beam)
+void h2_beam_set_copy_files(h2_bucket_beam * beam, int enabled)
 {
-    h2_beam_lock bl;
-    apr_size_t buffer_size = 0;
-    
-    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
-        buffer_size = beam->max_buf_size;
-        leave_yellow(beam, &bl);
-    }
-    return buffer_size;
+    apr_thread_mutex_lock(beam->lock);
+    beam->copy_files = enabled;
+    apr_thread_mutex_unlock(beam->lock);
 }
 
-void h2_beam_timeout_set(h2_bucket_beam *beam, apr_interval_time_t timeout)
+apr_size_t h2_beam_buffer_size_get(h2_bucket_beam *beam)
 {
-    h2_beam_lock bl;
+    apr_size_t buffer_size = 0;
     
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        beam->timeout = timeout;
-        leave_yellow(beam, &bl);
-    }
+    apr_thread_mutex_lock(beam->lock);
+    buffer_size = beam->max_buf_size;
+    apr_thread_mutex_unlock(beam->lock);
+    return buffer_size;
 }
 
 apr_interval_time_t h2_beam_timeout_get(h2_bucket_beam *beam)
 {
-    h2_beam_lock bl;
-    apr_interval_time_t timeout = 0;
-    
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        timeout = beam->timeout;
-        leave_yellow(beam, &bl);
-    }
-    return timeout;
-}
-
-void h2_beam_abort(h2_bucket_beam *beam)
-{
-    h2_beam_lock bl;
-    
-    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
-        beam->aborted = 1;
-        r_purge_sent(beam);
-        h2_blist_cleanup(&beam->send_list);
-        report_consumption(beam, &bl);
-        apr_thread_cond_broadcast(beam->change);
-        leave_yellow(beam, &bl);
-    }
-}
+    apr_interval_time_t timeout;
 
-apr_status_t h2_beam_close(h2_bucket_beam *beam)
-{
-    h2_beam_lock bl;
-    
-    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
-        r_purge_sent(beam);
-        beam_close(beam);
-        report_consumption(beam, &bl);
-        leave_yellow(beam, &bl);
-    }
-    return beam->aborted? APR_ECONNABORTED : APR_SUCCESS;
+    apr_thread_mutex_lock(beam->lock);
+    timeout = beam->timeout;
+    apr_thread_mutex_unlock(beam->lock);
+    return timeout;
 }
 
-apr_status_t h2_beam_leave(h2_bucket_beam *beam)
+void h2_beam_timeout_set(h2_bucket_beam *beam, apr_interval_time_t timeout)
 {
-    h2_beam_lock bl;
-    
-    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
-        recv_buffer_cleanup(beam, &bl);
-        beam->aborted = 1;
-        beam_close(beam);
-        leave_yellow(beam, &bl);
-    }
-    return APR_SUCCESS;
+    apr_thread_mutex_lock(beam->lock);
+    beam->timeout = timeout;
+    apr_thread_mutex_unlock(beam->lock);
 }
 
-apr_status_t h2_beam_wait_empty(h2_bucket_beam *beam, apr_read_type_e block)
+void h2_beam_abort(h2_bucket_beam *beam, conn_rec *c)
 {
-    apr_status_t status;
-    h2_beam_lock bl;
-    
-    if ((status = enter_yellow(beam, &bl)) == APR_SUCCESS) {
-        status = wait_empty(beam, block, bl.mutex);
-        leave_yellow(beam, &bl);
+    apr_thread_mutex_lock(beam->lock);
+    beam->aborted = 1;
+    if (c == beam->from) {
+        /* sender aborts */
+        if (beam->was_empty_cb && buffer_is_empty(beam)) {
+            beam->was_empty_cb(beam->was_empty_ctx, beam);
+        }
+        /* no more consumption reporting to sender */
+        beam->cons_io_cb = NULL;
+        beam->cons_ctx = NULL;
+        purge_consumed_buckets(beam);
+        h2_blist_cleanup(&beam->buckets_to_send);
+        report_consumption(beam, 1);
     }
-    return status;
-}
-
-static void move_to_hold(h2_bucket_beam *beam, 
-                         apr_bucket_brigade *sender_bb)
-{
-    apr_bucket *b;
-    while (sender_bb && !APR_BRIGADE_EMPTY(sender_bb)) {
-        b = APR_BRIGADE_FIRST(sender_bb);
-        APR_BUCKET_REMOVE(b);
-        H2_BLIST_INSERT_TAIL(&beam->send_list, b);
+    else {
+        /* receiver aborts */
+        recv_buffer_cleanup(beam);
     }
+    apr_thread_cond_broadcast(beam->change);
+    apr_thread_mutex_unlock(beam->lock);
 }
 
-static apr_status_t append_bucket(h2_bucket_beam *beam, 
+static apr_status_t append_bucket(h2_bucket_beam *beam,
                                   apr_bucket *b,
                                   apr_read_type_e block,
-                                  apr_size_t *pspace_left,
-                                  h2_beam_lock *pbl)
+                                  apr_size_t *pspace_left)
 {
     const char *data;
     apr_size_t len;
-    apr_status_t status;
+    apr_status_t status = APR_SUCCESS;
     int can_beam = 0, check_len;
     
     (void)block;
-    (void)pbl;
     if (beam->aborted) {
         return APR_ECONNABORTED;
     }
     
     if (APR_BUCKET_IS_METADATA(b)) {
-        if (APR_BUCKET_IS_EOS(b)) {
-            beam->closed = 1;
-        }
         APR_BUCKET_REMOVE(b);
-        H2_BLIST_INSERT_TAIL(&beam->send_list, b);
+        apr_bucket_setaside(b, beam->pool);
+        H2_BLIST_INSERT_TAIL(&beam->buckets_to_send, b);
         return APR_SUCCESS;
     }
     else if (APR_BUCKET_IS_FILE(b)) {
@@ -809,11 +457,11 @@ static apr_status_t append_bucket(h2_buc
          * of open file handles and rather use a less efficient beam
          * transport. */
         apr_bucket_file *bf = b->data;
-        apr_file_t *fd = bf->fd;
-        can_beam = (bf->refcount.refcount == 1);
-        if (can_beam && beam->can_beam_fn) {
-            can_beam = beam->can_beam_fn(beam->can_beam_ctx, beam, fd);
-        }
+        can_beam = !beam->copy_files && (bf->refcount.refcount == 1);
+        check_len = !can_beam;
+    }
+    else if (bucket_is_mmap(b)) {
+        can_beam = !beam->copy_files;
         check_len = !can_beam;
     }
     else {
@@ -838,453 +486,355 @@ static apr_status_t append_bucket(h2_buc
      * a receiver thread is a total NO GO, because the bucket might use
      * its pool/bucket_alloc from a foreign thread and that will
      * corrupt. */
-    status = APR_ENOTIMPL;
-    if (APR_BUCKET_IS_TRANSIENT(b)) {
-        /* this takes care of transient buckets and converts them
-         * into heap ones. Other bucket types might or might not be
-         * affected by this. */
-        status = apr_bucket_setaside(b, beam->send_pool);
+    if (b->length == 0) {
+        apr_bucket_delete(b);
+        return APR_SUCCESS;
     }
     else if (APR_BUCKET_IS_HEAP(b)) {
         /* For heap buckets read from a receiver thread is fine. The
          * data will be there and live until the bucket itself is
          * destroyed. */
-        status = APR_SUCCESS;
-    }
-    else if (APR_BUCKET_IS_POOL(b)) {
-        /* pool buckets are bastards that register at pool cleanup
-         * to morph themselves into heap buckets. That may happen anytime,
-         * even after the bucket data pointer has been read. So at
-         * any time inside the receiver thread, the pool bucket memory
-         * may disappear. yikes. */
-        status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
-        if (status == APR_SUCCESS) {
-            apr_bucket_heap_make(b, data, len, NULL);
-        }
+        status = apr_bucket_setaside(b, beam->pool);
+        if (status != APR_SUCCESS) goto cleanup;
     }
-    else if (APR_BUCKET_IS_FILE(b) && can_beam) {
-        status = apr_bucket_setaside(b, beam->send_pool);
+    else if (can_beam && (APR_BUCKET_IS_FILE(b) || bucket_is_mmap(b))) {
+        status = apr_bucket_setaside(b, beam->pool);
+        if (status != APR_SUCCESS) goto cleanup;
     }
-    
-    if (status == APR_ENOTIMPL) {
-        /* we have no knowledge about the internals of this bucket,
-         * but hope that after read, its data stays immutable for the
-         * lifetime of the bucket. (see pool bucket handling above for
-         * a counter example).
-         * We do the read while in the sender thread, so that the bucket may
-         * use pools/allocators safely. */
+    else {
+        /* we know of no special shortcut to transfer the bucket to
+         * another pool without copying. So we make it a heap bucket. */
         status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
-        if (status == APR_SUCCESS) {
-            status = apr_bucket_setaside(b, beam->send_pool);
-        }
-    }
-    
-    if (status != APR_SUCCESS && status != APR_ENOTIMPL) {
-        return status;
+        if (status != APR_SUCCESS) goto cleanup;
+        /* this allocates and copies data */
+        apr_bucket_heap_make(b, data, len, NULL);
     }
     
     APR_BUCKET_REMOVE(b);
-    H2_BLIST_INSERT_TAIL(&beam->send_list, b);
-    beam->sent_bytes += b->length;
-
-    return APR_SUCCESS;
-}
+    H2_BLIST_INSERT_TAIL(&beam->buckets_to_send, b);
 
-void h2_beam_send_from(h2_bucket_beam *beam, apr_pool_t *p)
-{
-    h2_beam_lock bl;
-    /* Called from the sender thread to add buckets to the beam */
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        r_purge_sent(beam);
-        beam_set_send_pool(beam, p);
-        leave_yellow(beam, &bl);
-    }
+cleanup:
+    return status;
 }
 
-apr_status_t h2_beam_send(h2_bucket_beam *beam, 
+apr_status_t h2_beam_send(h2_bucket_beam *beam, conn_rec *from,
                           apr_bucket_brigade *sender_bb, 
                           apr_read_type_e block)
 {
     apr_bucket *b;
     apr_status_t rv = APR_SUCCESS;
     apr_size_t space_left = 0;
-    h2_beam_lock bl;
+    int was_empty;
 
     /* Called from the sender thread to add buckets to the beam */
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        ap_assert(beam->send_pool);
-        r_purge_sent(beam);
-        
-        if (beam->aborted) {
-            move_to_hold(beam, sender_bb);
-            rv = APR_ECONNABORTED;
-        }
-        else if (sender_bb) {
-            int force_report = !APR_BRIGADE_EMPTY(sender_bb);
-            
-            space_left = calc_space_left(beam);
-            while (!APR_BRIGADE_EMPTY(sender_bb) && APR_SUCCESS == rv) {
-                if (space_left <= 0) {
-                    report_prod_io(beam, force_report, &bl);
-                    r_purge_sent(beam);
-                    rv = wait_not_full(beam, block, &space_left, &bl);
-                    if (APR_SUCCESS != rv) {
-                        break;
-                    }
-                }
-                b = APR_BRIGADE_FIRST(sender_bb);
-                rv = append_bucket(beam, b, block, &space_left, &bl);
+    apr_thread_mutex_lock(beam->lock);
+    ap_assert(beam->from == from);
+    ap_assert(sender_bb);
+    H2_BEAM_LOG(beam, from, APLOG_TRACE2, rv, "start send", sender_bb);
+    purge_consumed_buckets(beam);
+    was_empty = buffer_is_empty(beam);
+
+    space_left = calc_space_left(beam);
+    while (!APR_BRIGADE_EMPTY(sender_bb) && APR_SUCCESS == rv) {
+        if (!beam->aborted && space_left <= 0) {
+            purge_consumed_buckets(beam);
+            if (was_empty && beam->was_empty_cb) {
+                beam->was_empty_cb(beam->was_empty_ctx, beam);
             }
-            
-            report_prod_io(beam, force_report, &bl);
-            apr_thread_cond_broadcast(beam->change);
+            rv = wait_not_full(beam, from, block, &space_left);
+            if (APR_SUCCESS != rv) {
+                break;
+            }
+            was_empty = buffer_is_empty(beam);
         }
-        report_consumption(beam, &bl);
-        leave_yellow(beam, &bl);
+        b = APR_BRIGADE_FIRST(sender_bb);
+        rv = append_bucket(beam, b, block, &space_left);
+    }
+
+    if (was_empty && beam->was_empty_cb && !buffer_is_empty(beam)) {
+        beam->was_empty_cb(beam->was_empty_ctx, beam);
     }
+    apr_thread_cond_broadcast(beam->change);
+
+    report_consumption(beam, 1);
+    if (beam->aborted) {
+        rv = APR_ECONNABORTED;
+    }
+    H2_BEAM_LOG(beam, from, APLOG_TRACE2, rv, "end send", sender_bb);
+    apr_thread_mutex_unlock(beam->lock);
     return rv;
 }
 
-apr_status_t h2_beam_receive(h2_bucket_beam *beam, 
+apr_status_t h2_beam_receive(h2_bucket_beam *beam,
+                             conn_rec *to,
                              apr_bucket_brigade *bb, 
                              apr_read_type_e block,
-                             apr_off_t readbytes,
-                             int *pclosed)
+                             apr_off_t readbytes)
 {
-    h2_beam_lock bl;
     apr_bucket *bsender, *brecv, *ng;
     int transferred = 0;
-    apr_status_t status = APR_SUCCESS;
+    apr_status_t rv = APR_SUCCESS;
     apr_off_t remain;
-    int transferred_buckets = 0;
+    int consumed_buckets = 0;
+
+    apr_thread_mutex_lock(beam->lock);
+    H2_BEAM_LOG(beam, to, APLOG_TRACE2, 0, "start receive", bb);
+    if (readbytes <= 0) {
+        readbytes = (apr_off_t)APR_SIZE_MAX;
+    }
+    remain = readbytes;
 
-    /* Called from the receiver thread to take buckets from the beam */
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        if (readbytes <= 0) {
-            readbytes = (apr_off_t)APR_SIZE_MAX;
-        }
-        remain = readbytes;
-        
 transfer:
-        if (beam->aborted) {
-            recv_buffer_cleanup(beam, &bl);
-            status = APR_ECONNABORTED;
-            goto leave;
-        }
+    if (beam->aborted) {
+        recv_buffer_cleanup(beam);
+        rv = APR_ECONNABORTED;
+        goto leave;
+    }
 
-        /* transfer enough buckets from our receiver brigade, if we have one */
-        while (remain >= 0 
-               && beam->recv_buffer 
-               && !APR_BRIGADE_EMPTY(beam->recv_buffer)) {
-               
-            brecv = APR_BRIGADE_FIRST(beam->recv_buffer);
-            if (brecv->length > 0 && remain <= 0) {
-                break;
-            }            
-            APR_BUCKET_REMOVE(brecv);
-            APR_BRIGADE_INSERT_TAIL(bb, brecv);
-            remain -= brecv->length;
-            ++transferred;
+    /* transfer enough buckets from our receiver brigade, if we have one */
+    while (remain >= 0
+           && beam->recv_buffer
+           && !APR_BRIGADE_EMPTY(beam->recv_buffer)) {
+
+        brecv = APR_BRIGADE_FIRST(beam->recv_buffer);
+        if (brecv->length > 0 && remain <= 0) {
+            break;
         }
+        APR_BUCKET_REMOVE(brecv);
+        APR_BRIGADE_INSERT_TAIL(bb, brecv);
+        remain -= brecv->length;
+        ++transferred;
+    }
 
-        /* transfer from our sender brigade, transforming sender buckets to
-         * receiver ones until we have enough */
-        while (remain >= 0 && !H2_BLIST_EMPTY(&beam->send_list)) {
-               
-            brecv = NULL;
-            bsender = H2_BLIST_FIRST(&beam->send_list);            
-            if (bsender->length > 0 && remain <= 0) {
-                break;
-            }
-                        
-            if (APR_BUCKET_IS_METADATA(bsender)) {
-                if (APR_BUCKET_IS_EOS(bsender)) {
-                    brecv = apr_bucket_eos_create(bb->bucket_alloc);
-                    beam->close_sent = 1;
-                }
-                else if (APR_BUCKET_IS_FLUSH(bsender)) {
-                    brecv = apr_bucket_flush_create(bb->bucket_alloc);
-                }
-                else if (AP_BUCKET_IS_ERROR(bsender)) {
-                    ap_bucket_error *eb = (ap_bucket_error *)bsender;
-                    brecv = ap_bucket_error_create(eb->status, eb->data,
-                                                    bb->p, bb->bucket_alloc);
-                }
-            }
-            else if (bsender->length == 0) {
-                APR_BUCKET_REMOVE(bsender);
-                H2_BLIST_INSERT_TAIL(&beam->hold_list, bsender);
-                continue;
-            }
-            else if (APR_BUCKET_IS_FILE(bsender)) {
-                /* This is set aside into the target brigade pool so that 
-                 * any read operation messes with that pool and not 
-                 * the sender one. */
-                apr_bucket_file *f = (apr_bucket_file *)bsender->data;
-                apr_file_t *fd = f->fd;
-                int setaside = (f->readpool != bb->p);
-                
-                if (setaside) {
-                    status = apr_file_setaside(&fd, fd, bb->p);
-                    if (status != APR_SUCCESS) {
-                        goto leave;
-                    }
-                    ++beam->files_beamed;
-                }
-                ng = apr_brigade_insert_file(bb, fd, bsender->start, (apr_off_t)bsender->length, 
-                                             bb->p);
-#if APR_HAS_MMAP
-                /* disable mmap handling as this leads to segfaults when
-                 * the underlying file is changed while memory pointer has
-                 * been handed out. See also PR 59348 */
-                apr_bucket_file_enable_mmap(ng, 0);
-#endif
-                APR_BUCKET_REMOVE(bsender);
-                H2_BLIST_INSERT_TAIL(&beam->hold_list, bsender);
+    /* transfer from our sender brigade, transforming sender buckets to
+     * receiver ones until we have enough */
+    while (remain >= 0 && !H2_BLIST_EMPTY(&beam->buckets_to_send)) {
 
-                remain -= bsender->length;
-                beam->received_bytes += bsender->length;
-                ++transferred;
-                ++transferred_buckets;
-                continue;
+        brecv = NULL;
+        bsender = H2_BLIST_FIRST(&beam->buckets_to_send);
+        if (bsender->length > 0 && remain <= 0) {
+            break;
+        }
+
+        if (APR_BUCKET_IS_METADATA(bsender)) {
+            /* we need a real copy into the receivers bucket_alloc */
+            if (APR_BUCKET_IS_EOS(bsender)) {
+                brecv = apr_bucket_eos_create(bb->bucket_alloc);
             }
-            else {
-                /* create a "receiver" standin bucket. we took care about the
-                 * underlying sender bucket and its data when we placed it into
-                 * the sender brigade.
-                 * the beam bucket will notify us on destruction that bsender is
-                 * no longer needed. */
-                brecv = h2_beam_bucket_create(beam, bsender, bb->bucket_alloc,
-                                               beam->buckets_sent++);
+            else if (APR_BUCKET_IS_FLUSH(bsender)) {
+                brecv = apr_bucket_flush_create(bb->bucket_alloc);
             }
-            
-            /* Place the sender bucket into our hold, to be destroyed when no
-             * receiver bucket references it any more. */
-            APR_BUCKET_REMOVE(bsender);
-            H2_BLIST_INSERT_TAIL(&beam->hold_list, bsender);
-            
-            beam->received_bytes += bsender->length;
-            ++transferred_buckets;
-            
-            if (brecv) {
-                APR_BRIGADE_INSERT_TAIL(bb, brecv);
-                remain -= brecv->length;
-                ++transferred;
+            else if (AP_BUCKET_IS_ERROR(bsender)) {
+                ap_bucket_error *eb = (ap_bucket_error *)bsender;
+                brecv = ap_bucket_error_create(eb->status, eb->data,
+                                                bb->p, bb->bucket_alloc);
             }
             else {
-                /* let outside hook determine how bucket is beamed */
-                leave_yellow(beam, &bl);
+                /* Does someone else know how to make a proxy for
+                 * the bucket? Ask the callbacks registered for this. */
                 brecv = h2_beam_bucket(beam, bb, bsender);
-                enter_yellow(beam, &bl);
-                
                 while (brecv && brecv != APR_BRIGADE_SENTINEL(bb)) {
                     ++transferred;
                     remain -= brecv->length;
                     brecv = APR_BUCKET_NEXT(brecv);
                 }
+                brecv = NULL;
             }
         }
-
-        if (remain < 0) {
-            /* too much, put some back into out recv_buffer */
-            remain = readbytes;
-            for (brecv = APR_BRIGADE_FIRST(bb);
-                 brecv != APR_BRIGADE_SENTINEL(bb);
-                 brecv = APR_BUCKET_NEXT(brecv)) {
-                remain -= (beam->tx_mem_limits? bucket_mem_used(brecv) 
-                           : (apr_off_t)brecv->length);
-                if (remain < 0) {
-                    apr_bucket_split(brecv, (apr_size_t)((apr_off_t)brecv->length+remain));
-                    beam->recv_buffer = apr_brigade_split_ex(bb, 
-                                                             APR_BUCKET_NEXT(brecv), 
-                                                             beam->recv_buffer);
-                    break;
-                }
-            }
+        else if (bsender->length == 0) {
+            /* nop */
         }
-
-        if (beam->closed && buffer_is_empty(beam)) {
-            /* beam is closed and we have nothing more to receive */ 
-            if (!beam->close_sent) {
-                apr_bucket *b = apr_bucket_eos_create(bb->bucket_alloc);
-                APR_BRIGADE_INSERT_TAIL(bb, b);
-                beam->close_sent = 1;
-                ++transferred;
-                status = APR_SUCCESS;
-            }
+#if APR_HAS_MMAP
+        else if (APR_BUCKET_IS_MMAP(bsender)) {
+            apr_bucket_mmap *bmmap = bsender->data;
+            apr_mmap_t *mmap;
+            rv = apr_mmap_dup(&mmap, bmmap->mmap, bb->p);
+            if (rv != APR_SUCCESS) goto leave;
+            brecv = apr_bucket_mmap_create(mmap, bsender->start, bsender->length, bb->bucket_alloc);
         }
-        
-        if (transferred_buckets > 0) {
-           if (beam->cons_ev_cb) { 
-               beam->cons_ev_cb(beam->cons_ctx, beam);
+#endif
+        else if (APR_BUCKET_IS_FILE(bsender)) {
+            /* This is setaside into the target brigade pool so that
+             * any read operation messes with that pool and not
+             * the sender one. */
+            apr_bucket_file *f = (apr_bucket_file *)bsender->data;
+            apr_file_t *fd = f->fd;
+            int setaside = (f->readpool != bb->p);
+
+            if (setaside) {
+                rv = apr_file_setaside(&fd, fd, bb->p);
+                if (rv != APR_SUCCESS) goto leave;
             }
-        }
-        
-        if (transferred) {
-            apr_thread_cond_broadcast(beam->change);
-            status = APR_SUCCESS;
+            ng = apr_brigade_insert_file(bb, fd, bsender->start, (apr_off_t)bsender->length,
+                                         bb->p);
+#if APR_HAS_MMAP
+            /* disable mmap handling as this leads to segfaults when
+             * the underlying file is changed while memory pointer has
+             * been handed out. See also PR 59348 */
+            apr_bucket_file_enable_mmap(ng, 0);
+#endif
+            remain -= bsender->length;
+            ++transferred;
         }
         else {
-            status = wait_not_empty(beam, block, bl.mutex);
-            if (status != APR_SUCCESS) {
-                goto leave;
+            const char *data;
+            apr_size_t dlen;
+            /* we did that when the bucket was added, so this should
+             * give us the same data as before without changing the bucket
+             * or anything (pool) connected to it. */
+            rv = apr_bucket_read(bsender, &data, &dlen, APR_BLOCK_READ);
+            if (rv != APR_SUCCESS) goto leave;
+            rv = apr_brigade_write(bb, NULL, NULL, data, dlen);
+            if (rv != APR_SUCCESS) goto leave;
+
+            remain -= dlen;
+            ++transferred;
+        }
+
+        if (brecv) {
+            /* we have a proxy that we can give the receiver */
+            APR_BRIGADE_INSERT_TAIL(bb, brecv);
+            remain -= brecv->length;
+            ++transferred;
+        }
+        APR_BUCKET_REMOVE(bsender);
+        H2_BLIST_INSERT_TAIL(&beam->buckets_consumed, bsender);
+        beam->recv_bytes += bsender->length;
+        ++consumed_buckets;
+    }
+
+    if (remain < 0) {
+        /* too much, put some back into out recv_buffer */
+        remain = readbytes;
+        for (brecv = APR_BRIGADE_FIRST(bb);
+             brecv != APR_BRIGADE_SENTINEL(bb);
+             brecv = APR_BUCKET_NEXT(brecv)) {
+            remain -= (beam->tx_mem_limits? bucket_mem_used(brecv)
+                       : (apr_off_t)brecv->length);
+            if (remain < 0) {
+                apr_bucket_split(brecv, (apr_size_t)((apr_off_t)brecv->length+remain));
+                beam->recv_buffer = apr_brigade_split_ex(bb,
+                                                         APR_BUCKET_NEXT(brecv),
+                                                         beam->recv_buffer);
+                break;
             }
-            goto transfer;
         }
-leave:
-        if (pclosed) *pclosed = beam->closed? 1 : 0;
-        leave_yellow(beam, &bl);
     }
-    return status;
+
+    if (beam->recv_cb && consumed_buckets > 0) {
+        beam->recv_cb(beam->recv_ctx, beam);
+    }
+
+    if (transferred) {
+        apr_thread_cond_broadcast(beam->change);
+        rv = APR_SUCCESS;
+    }
+    else if (beam->aborted) {
+        rv = APR_ECONNABORTED;
+    }
+    else {
+        rv = wait_not_empty(beam, to, block);
+        if (rv != APR_SUCCESS) {
+            goto leave;
+        }
+        goto transfer;
+    }
+
+leave:
+    H2_BEAM_LOG(beam, to, APLOG_TRACE2, rv, "end receive", bb);
+    apr_thread_mutex_unlock(beam->lock);
+    return rv;
 }
 
 void h2_beam_on_consumed(h2_bucket_beam *beam, 
-                         h2_beam_ev_callback *ev_cb,
                          h2_beam_io_callback *io_cb, void *ctx)
 {
-    h2_beam_lock bl;
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        beam->cons_ev_cb = ev_cb;
-        beam->cons_io_cb = io_cb;
-        beam->cons_ctx = ctx;
-        leave_yellow(beam, &bl);
-    }
+    apr_thread_mutex_lock(beam->lock);
+    beam->cons_io_cb = io_cb;
+    beam->cons_ctx = ctx;
+    apr_thread_mutex_unlock(beam->lock);
 }
 
-void h2_beam_on_produced(h2_bucket_beam *beam, 
-                         h2_beam_io_callback *io_cb, void *ctx)
+void h2_beam_on_received(h2_bucket_beam *beam,
+                         h2_beam_ev_callback *recv_cb, void *ctx)
 {
-    h2_beam_lock bl;
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        beam->prod_io_cb = io_cb;
-        beam->prod_ctx = ctx;
-        leave_yellow(beam, &bl);
-    }
+    apr_thread_mutex_lock(beam->lock);
+    beam->recv_cb = recv_cb;
+    beam->recv_ctx = ctx;
+    apr_thread_mutex_unlock(beam->lock);
 }
 
-void h2_beam_on_file_beam(h2_bucket_beam *beam, 
-                          h2_beam_can_beam_callback *cb, void *ctx)
+void h2_beam_on_was_empty(h2_bucket_beam *beam,
+                          h2_beam_ev_callback *was_empty_cb, void *ctx)
 {
-    h2_beam_lock bl;
-    
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        beam->can_beam_fn = cb;
-        beam->can_beam_ctx = ctx;
-        leave_yellow(beam, &bl);
-    }
+    apr_thread_mutex_lock(beam->lock);
+    beam->was_empty_cb = was_empty_cb;
+    beam->was_empty_ctx = ctx;
+    apr_thread_mutex_unlock(beam->lock);
 }
 
 
-apr_off_t h2_beam_get_buffered(h2_bucket_beam *beam)
+static apr_off_t get_buffered_data_len(h2_bucket_beam *beam)
 {
     apr_bucket *b;
     apr_off_t l = 0;
-    h2_beam_lock bl;
-    
-    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
-        for (b = H2_BLIST_FIRST(&beam->send_list); 
-            b != H2_BLIST_SENTINEL(&beam->send_list);
-            b = APR_BUCKET_NEXT(b)) {
-            /* should all have determinate length */
-            l += b->length;
-        }
-        leave_yellow(beam, &bl);
+
+    for (b = H2_BLIST_FIRST(&beam->buckets_to_send);
+        b != H2_BLIST_SENTINEL(&beam->buckets_to_send);
+        b = APR_BUCKET_NEXT(b)) {
+        /* should all have determinate length */
+        l += b->length;
     }
     return l;
 }
 
-apr_off_t h2_beam_get_mem_used(h2_bucket_beam *beam)
+apr_off_t h2_beam_get_buffered(h2_bucket_beam *beam)
 {
-    apr_bucket *b;
     apr_off_t l = 0;
-    h2_beam_lock bl;
-    
-    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
-        for (b = H2_BLIST_FIRST(&beam->send_list); 
-            b != H2_BLIST_SENTINEL(&beam->send_list);
-            b = APR_BUCKET_NEXT(b)) {
-            l += bucket_mem_used(b);
-        }
-        leave_yellow(beam, &bl);
-    }
+
+    apr_thread_mutex_lock(beam->lock);
+    l = get_buffered_data_len(beam);
+    apr_thread_mutex_unlock(beam->lock);
     return l;
 }
 
-int h2_beam_empty(h2_bucket_beam *beam)
+apr_off_t h2_beam_get_mem_used(h2_bucket_beam *beam)
 {
-    int empty = 1;
-    h2_beam_lock bl;
-    
-    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
-        empty = (H2_BLIST_EMPTY(&beam->send_list) 
-                 && (!beam->recv_buffer || APR_BRIGADE_EMPTY(beam->recv_buffer)));
-        leave_yellow(beam, &bl);
-    }
-    return empty;
-}
+    apr_bucket *b;
+    apr_off_t l = 0;
 
-int h2_beam_holds_proxies(h2_bucket_beam *beam)
-{
-    int has_proxies = 1;
-    h2_beam_lock bl;
-    
-    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
-        has_proxies = !H2_BPROXY_LIST_EMPTY(&beam->proxies);
-        leave_yellow(beam, &bl);
+    apr_thread_mutex_lock(beam->lock);
+    for (b = H2_BLIST_FIRST(&beam->buckets_to_send);
+        b != H2_BLIST_SENTINEL(&beam->buckets_to_send);
+        b = APR_BUCKET_NEXT(b)) {
+        l += bucket_mem_used(b);
     }
-    return has_proxies;
+    apr_thread_mutex_unlock(beam->lock);
+    return l;
 }
 
-int h2_beam_was_received(h2_bucket_beam *beam)
+static int is_empty(h2_bucket_beam *beam)
 {
-    int happend = 0;
-    h2_beam_lock bl;
-    
-    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
-        happend = (beam->received_bytes > 0);
-        leave_yellow(beam, &bl);
-    }
-    return happend;
+    return (H2_BLIST_EMPTY(&beam->buckets_to_send)
+            && (!beam->recv_buffer || APR_BRIGADE_EMPTY(beam->recv_buffer)));
 }
 
-apr_size_t h2_beam_get_files_beamed(h2_bucket_beam *beam)
+int h2_beam_empty(h2_bucket_beam *beam)
 {
-    apr_size_t n = 0;
-    h2_beam_lock bl;
-    
-    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
-        n = beam->files_beamed;
-        leave_yellow(beam, &bl);
-    }
-    return n;
-}
+    int empty = 1;
 
-int h2_beam_no_files(void *ctx, h2_bucket_beam *beam, apr_file_t *file)
-{
-    (void)ctx; (void)beam; (void)file;
-    return 0;
+    apr_thread_mutex_lock(beam->lock);
+    empty = is_empty(beam);
+    apr_thread_mutex_unlock(beam->lock);
+    return empty;
 }
 
 int h2_beam_report_consumption(h2_bucket_beam *beam)
 {
-    h2_beam_lock bl;
     int rv = 0;
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        rv = report_consumption(beam, &bl);
-        leave_yellow(beam, &bl);
-    }
-    return rv;
-}
 
-void h2_beam_log(h2_bucket_beam *beam, conn_rec *c, int level, const char *msg)
-{
-    if (beam && APLOG_C_IS_LEVEL(c,level)) {
-        ap_log_cerror(APLOG_MARK, level, 0, c, 
-                      "beam(%ld-%d,%s,closed=%d,aborted=%d,empty=%d,buf=%ld): %s", 
-                      (c->master? c->master->id : c->id), beam->id, beam->tag, 
-                      beam->closed, beam->aborted, h2_beam_empty(beam), 
-                      (long)h2_beam_get_buffered(beam), msg);
-    }
+    apr_thread_mutex_lock(beam->lock);
+    rv = report_consumption(beam, 1);
+    apr_thread_mutex_unlock(beam->lock);
+    return rv;
 }
-
-



Re: svn commit: r1894163 [1/8] - in /httpd/httpd/trunk: ./ changes-entries/ modules/http2/ test/modules/http2/

Posted by "stefan@eissing.org" <st...@eissing.org>.

> Am 13.10.2021 um 10:14 schrieb Ruediger Pluem <rp...@apache.org>:
> 
> 
> 
> On 10/12/21 3:34 PM, icing@apache.org wrote:
>> Author: icing
>> Date: Tue Oct 12 13:34:01 2021
>> New Revision: 1894163
>> 
>> URL: http://svn.apache.org/viewvc?rev=1894163&view=rev
>> Log:
>>  *) mod_http2:
>>     - Fixed an issue since 1.15.24 that "Server" headers in proxied requests
>>       were overwritten instead of preserved. [PR by @daum3ns]
>>     - Added directove 'H2StreamTimeout' to configure a separate value for HTTP/2
>>       streams, overriding server's 'Timeout' configuration. [rpluem]
>>     - HTTP/2 connections now use pollsets to monitor the status of the
>>       ongoing streams and their main connection when host OS allows this.
>>     - Removed work-arounds for older versions of libnghttp2 and checking
>>       during configure that at least version 1.15.0 is present.
> 
> I guess this means that we need to remove mod_http2 from our Xenial Travis builds since it only ships with 1.7.1.
> Or should we remove the one remaining Xenial build at all?
> At least it has the benefit that it tells us that things break if we move forward with dependencies :-)

Yeah, saw that. I am bringing back the checks for earlier nghttp2 libs right now.

Praise to the Travis hamsters!

> 
> Regards
> 
> Rüdiger


Re: svn commit: r1894163 [1/8] - in /httpd/httpd/trunk: ./ changes-entries/ modules/http2/ test/modules/http2/

Posted by Ruediger Pluem <rp...@apache.org>.

On 10/12/21 3:34 PM, icing@apache.org wrote:
> Author: icing
> Date: Tue Oct 12 13:34:01 2021
> New Revision: 1894163
> 
> URL: http://svn.apache.org/viewvc?rev=1894163&view=rev
> Log:
>   *) mod_http2:
>      - Fixed an issue since 1.15.24 that "Server" headers in proxied requests
>        were overwritten instead of preserved. [PR by @daum3ns]
>      - Added directove 'H2StreamTimeout' to configure a separate value for HTTP/2
>        streams, overriding server's 'Timeout' configuration. [rpluem]
>      - HTTP/2 connections now use pollsets to monitor the status of the
>        ongoing streams and their main connection when host OS allows this.
>      - Removed work-arounds for older versions of libnghttp2 and checking
>        during configure that at least version 1.15.0 is present.

I guess this means that we need to remove mod_http2 from our Xenial Travis builds since it only ships with 1.7.1.
Or should we remove the one remaining Xenial build at all?
At least it has the benefit that it tells us that things break if we move forward with dependencies :-)

Regards

Rüdiger

Re: svn commit: r1894163 [1/8] - in /httpd/httpd/trunk: ./ changes-entries/ modules/http2/ test/modules/http2/

Posted by Ruediger Pluem <rp...@apache.org>.

On 10/14/21 11:26 AM, stefan@eissing.org wrote:
> 
> 
>> Am 14.10.2021 um 11:23 schrieb Ruediger Pluem <rp...@apache.org>:
>>
>>
>>
>> On 10/14/21 11:16 AM, stefan@eissing.org wrote:
>>>
>>>
>>>> Am 14.10.2021 um 11:07 schrieb Ruediger Pluem <rp...@apache.org>:
>>>>
>>>>
>>>>
>>>> On 10/12/21 3:34 PM, icing@apache.org wrote:
>>>>> Author: icing
>>>>> Date: Tue Oct 12 13:34:01 2021
>>>>> New Revision: 1894163
>>
>>>>>
>>>>> static apr_off_t bucket_mem_used(apr_bucket *b)
>>>>> {
>>>>> -    if (APR_BUCKET_IS_FILE(b)) {
>>>>> +    if (APR_BUCKET_IS_FILE(b) || bucket_is_mmap(b)) {
>>>>
>>>> MMaped buckets also consume physical memory once the content was read. Of course the pages can be dropped from physical memory
>>>> again if space is needed. Furthermore they consume address space in the process, but I admit that this will unlikely cause
>>>> any issues on 64 bit architectures. The only thing that can happen here is that people complain about large virtual memory sizes
>>>> of processes which has no real impact in this case.
>>>
>>> My (maybe faulty) reasoning here is: 
>>> HTTP/2 does not create any FILE or MMAP buckets. It just wants to transfer them efficiently from c2 to c1. Instead of reading them and copying the chunks, it apr_mmap_dup() or apr_file_setaside() their content for the receiving bucket brigade. 
>>>
>>> This should (as I understand it) not duplicate any memory or use more memory than in a HTTP/1.1 response (plus the usual h2 overhead).
>>
>> Agreed, but reading consumers need to take care that they do not read too much at once without dropping the mmap bucket(s) after
>> reading, but this is not mod_http2's responsibility.
>> This leaves only the address space issue, which should only matter on 32 bit architectures in certain cases. So I guess this could
>> stay as is until people from non 64 bit platforms complain :-)
> 
> Isn't that what "EnableMMAP off" is for?

From my point of view not really. MMap does not work well with files on certain network file systems like NFS if file sizes change
while the file is open. This could cause core dumps. Hence the possibility to switch it off.

Regards

Rüdiger

Re: svn commit: r1894163 [1/8] - in /httpd/httpd/trunk: ./ changes-entries/ modules/http2/ test/modules/http2/

Posted by "stefan@eissing.org" <st...@eissing.org>.

> Am 14.10.2021 um 11:23 schrieb Ruediger Pluem <rp...@apache.org>:
> 
> 
> 
> On 10/14/21 11:16 AM, stefan@eissing.org wrote:
>> 
>> 
>>> Am 14.10.2021 um 11:07 schrieb Ruediger Pluem <rp...@apache.org>:
>>> 
>>> 
>>> 
>>> On 10/12/21 3:34 PM, icing@apache.org wrote:
>>>> Author: icing
>>>> Date: Tue Oct 12 13:34:01 2021
>>>> New Revision: 1894163
> 
>>>> 
>>>> static apr_off_t bucket_mem_used(apr_bucket *b)
>>>> {
>>>> -    if (APR_BUCKET_IS_FILE(b)) {
>>>> +    if (APR_BUCKET_IS_FILE(b) || bucket_is_mmap(b)) {
>>> 
>>> MMaped buckets also consume physical memory once the content was read. Of course the pages can be dropped from physical memory
>>> again if space is needed. Furthermore they consume address space in the process, but I admit that this will unlikely cause
>>> any issues on 64 bit architectures. The only thing that can happen here is that people complain about large virtual memory sizes
>>> of processes which has no real impact in this case.
>> 
>> My (maybe faulty) reasoning here is: 
>> HTTP/2 does not create any FILE or MMAP buckets. It just wants to transfer them efficiently from c2 to c1. Instead of reading them and copying the chunks, it apr_mmap_dup() or apr_file_setaside() their content for the receiving bucket brigade. 
>> 
>> This should (as I understand it) not duplicate any memory or use more memory than in a HTTP/1.1 response (plus the usual h2 overhead).
> 
> Agreed, but reading consumers need to take care that they do not read too much at once without dropping the mmap bucket(s) after
> reading, but this is not mod_http2's responsibility.
> This leaves only the address space issue, which should only matter on 32 bit architectures in certain cases. So I guess this could
> stay as is until people from non 64 bit platforms complain :-)

Isn't that what "EnableMMAP off" is for?

> 
> 
> Regards
> 
> Rüdiger
> 


Re: svn commit: r1894163 [1/8] - in /httpd/httpd/trunk: ./ changes-entries/ modules/http2/ test/modules/http2/

Posted by Ruediger Pluem <rp...@apache.org>.

On 10/14/21 11:16 AM, stefan@eissing.org wrote:
> 
> 
>> Am 14.10.2021 um 11:07 schrieb Ruediger Pluem <rp...@apache.org>:
>>
>>
>>
>> On 10/12/21 3:34 PM, icing@apache.org wrote:
>>> Author: icing
>>> Date: Tue Oct 12 13:34:01 2021
>>> New Revision: 1894163

>>>
>>> static apr_off_t bucket_mem_used(apr_bucket *b)
>>> {
>>> -    if (APR_BUCKET_IS_FILE(b)) {
>>> +    if (APR_BUCKET_IS_FILE(b) || bucket_is_mmap(b)) {
>>
>> MMaped buckets also consume physical memory once the content was read. Of course the pages can be dropped from physical memory
>> again if space is needed. Furthermore they consume address space in the process, but I admit that this will unlikely cause
>> any issues on 64 bit architectures. The only thing that can happen here is that people complain about large virtual memory sizes
>> of processes which has no real impact in this case.
> 
> My (maybe faulty) reasoning here is: 
> HTTP/2 does not create any FILE or MMAP buckets. It just wants to transfer them efficiently from c2 to c1. Instead of reading them and copying the chunks, it apr_mmap_dup() or apr_file_setaside() their content for the receiving bucket brigade. 
> 
> This should (as I understand it) not duplicate any memory or use more memory than in a HTTP/1.1 response (plus the usual h2 overhead).

Agreed, but reading consumers need to take care that they do not read too much at once without dropping the mmap bucket(s) after
reading, but this is not mod_http2's responsibility.
This leaves only the address space issue, which should only matter on 32 bit architectures in certain cases. So I guess this could
stay as is until people from non 64 bit platforms complain :-)


Regards

Rüdiger


Re: svn commit: r1894163 [1/8] - in /httpd/httpd/trunk: ./ changes-entries/ modules/http2/ test/modules/http2/

Posted by "stefan@eissing.org" <st...@eissing.org>.

> Am 14.10.2021 um 11:07 schrieb Ruediger Pluem <rp...@apache.org>:
> 
> 
> 
> On 10/12/21 3:34 PM, icing@apache.org wrote:
>> Author: icing
>> Date: Tue Oct 12 13:34:01 2021
>> New Revision: 1894163
>> 
>> URL: http://svn.apache.org/viewvc?rev=1894163&view=rev
>> Log:
>>  *) mod_http2:
>>     - Fixed an issue since 1.15.24 that "Server" headers in proxied requests
>>       were overwritten instead of preserved. [PR by @daum3ns]
>>     - Added directove 'H2StreamTimeout' to configure a separate value for HTTP/2
>>       streams, overriding server's 'Timeout' configuration. [rpluem]
>>     - HTTP/2 connections now use pollsets to monitor the status of the
>>       ongoing streams and their main connection when host OS allows this.
>>     - Removed work-arounds for older versions of libnghttp2 and checking
>>       during configure that at least version 1.15.0 is present.
>>     - The HTTP/2 connection state handler, based on an experiment and draft
>>       at the IETF http working group (abandoned for some time), has been removed.
>>     - H2SerializeHeaders no longer has an effect. A warning is logged when it is
>>       set to "on". The switch enabled the internal writing of requests to be parsed
>>       by the internal HTTP/1.1 protocol handler and was introduced to avoid
>>       potential incompatibilities during the introduction of HTTP/2.
>>     - Removed the abort/redo of tasks when mood swings lower the active limit.
>> 
>> 
>> Added:
>>    httpd/httpd/trunk/changes-entries/http2_additions.txt
>>    httpd/httpd/trunk/modules/http2/h2_c1.c
>>    httpd/httpd/trunk/modules/http2/h2_c1.h
>>    httpd/httpd/trunk/modules/http2/h2_c1_io.c
>>    httpd/httpd/trunk/modules/http2/h2_c1_io.h
>>    httpd/httpd/trunk/modules/http2/h2_c2.c
>>    httpd/httpd/trunk/modules/http2/h2_c2.h
>>    httpd/httpd/trunk/modules/http2/h2_c2_filter.c
>>    httpd/httpd/trunk/modules/http2/h2_c2_filter.h
>>    httpd/httpd/trunk/modules/http2/h2_conn_ctx.c
>>    httpd/httpd/trunk/modules/http2/h2_conn_ctx.h
>>    httpd/httpd/trunk/modules/http2/h2_protocol.c
>>    httpd/httpd/trunk/modules/http2/h2_protocol.h
>> Removed:
>>    httpd/httpd/trunk/modules/http2/h2_alt_svc.c
>>    httpd/httpd/trunk/modules/http2/h2_alt_svc.h
>>    httpd/httpd/trunk/modules/http2/h2_conn.c
>>    httpd/httpd/trunk/modules/http2/h2_conn.h
>>    httpd/httpd/trunk/modules/http2/h2_conn_io.c
>>    httpd/httpd/trunk/modules/http2/h2_conn_io.h
>>    httpd/httpd/trunk/modules/http2/h2_ctx.c
>>    httpd/httpd/trunk/modules/http2/h2_ctx.h
>>    httpd/httpd/trunk/modules/http2/h2_filter.c
>>    httpd/httpd/trunk/modules/http2/h2_filter.h
>>    httpd/httpd/trunk/modules/http2/h2_from_h1.c
>>    httpd/httpd/trunk/modules/http2/h2_from_h1.h
>>    httpd/httpd/trunk/modules/http2/h2_h2.c
>>    httpd/httpd/trunk/modules/http2/h2_h2.h
>>    httpd/httpd/trunk/modules/http2/h2_task.c
>>    httpd/httpd/trunk/modules/http2/h2_task.h
>> Modified:
>>    httpd/httpd/trunk/CMakeLists.txt
>>    httpd/httpd/trunk/modules/http2/NWGNUmod_http2
>>    httpd/httpd/trunk/modules/http2/config2.m4
>>    httpd/httpd/trunk/modules/http2/h2.h
>>    httpd/httpd/trunk/modules/http2/h2_bucket_beam.c
>>    httpd/httpd/trunk/modules/http2/h2_bucket_beam.h
>>    httpd/httpd/trunk/modules/http2/h2_config.c
>>    httpd/httpd/trunk/modules/http2/h2_config.h
>>    httpd/httpd/trunk/modules/http2/h2_headers.c
>>    httpd/httpd/trunk/modules/http2/h2_mplx.c
>>    httpd/httpd/trunk/modules/http2/h2_mplx.h
>>    httpd/httpd/trunk/modules/http2/h2_proxy_session.c
>>    httpd/httpd/trunk/modules/http2/h2_proxy_util.c
>>    httpd/httpd/trunk/modules/http2/h2_proxy_util.h
>>    httpd/httpd/trunk/modules/http2/h2_push.c
>>    httpd/httpd/trunk/modules/http2/h2_request.c
>>    httpd/httpd/trunk/modules/http2/h2_request.h
>>    httpd/httpd/trunk/modules/http2/h2_session.c
>>    httpd/httpd/trunk/modules/http2/h2_session.h
>>    httpd/httpd/trunk/modules/http2/h2_stream.c
>>    httpd/httpd/trunk/modules/http2/h2_stream.h
>>    httpd/httpd/trunk/modules/http2/h2_switch.c
>>    httpd/httpd/trunk/modules/http2/h2_util.c
>>    httpd/httpd/trunk/modules/http2/h2_util.h
>>    httpd/httpd/trunk/modules/http2/h2_version.h
>>    httpd/httpd/trunk/modules/http2/h2_workers.c
>>    httpd/httpd/trunk/modules/http2/h2_workers.h
>>    httpd/httpd/trunk/modules/http2/mod_http2.c
>>    httpd/httpd/trunk/modules/http2/mod_http2.dsp
>>    httpd/httpd/trunk/modules/http2/mod_proxy_http2.c
>>    httpd/httpd/trunk/test/modules/http2/test_105_timeout.py
>>    httpd/httpd/trunk/test/modules/http2/test_712_buffering.py
>> 
>> Modified: httpd/httpd/trunk/CMakeLists.txt
>> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CMakeLists.txt?rev=1894163&r1=1894162&r2=1894163&view=diff
>> ==============================================================================
>> --- httpd/httpd/trunk/CMakeLists.txt (original)
>> +++ httpd/httpd/trunk/CMakeLists.txt Tue Oct 12 13:34:01 2021
>> @@ -469,18 +469,15 @@ SET(mod_http2_extra_defines          ssi
>> SET(mod_http2_extra_includes         ${NGHTTP2_INCLUDE_DIR})
>> SET(mod_http2_extra_libs             ${NGHTTP2_LIBRARIES})
>> SET(mod_http2_extra_sources
>> -  modules/http2/h2_alt_svc.c
>> -  modules/http2/h2_bucket_eos.c      modules/http2/h2_config.c
>> -  modules/http2/h2_conn.c            modules/http2/h2_conn_io.c
>> -  modules/http2/h2_ctx.c             modules/http2/h2_filter.c
>> -  modules/http2/h2_from_h1.c         modules/http2/h2_h2.c
>> -  modules/http2/h2_bucket_beam.c
>> -  modules/http2/h2_mplx.c            modules/http2/h2_push.c
>> -  modules/http2/h2_request.c         modules/http2/h2_headers.c
>> -  modules/http2/h2_session.c         modules/http2/h2_stream.c 
>> -  modules/http2/h2_switch.c
>> -  modules/http2/h2_task.c            modules/http2/h2_util.c
>> -  modules/http2/h2_workers.c
>> +  modules/http2/h2_bucket_beam.c     modules/http2/h2_bucket_eos.c
>> +  modules/http2/h2_c1.c              modules/http2/h2_c1_io.c
>> +  modules/http2/h2_c2.c              modules/http2/h2_c2_filter.c
>> +  modules/http2/h2_config.c          modules/http2/h2_conn_ctx.c
>> +  modules/http2/h2_headers.c         modules/http2/h2_mplx.c
>> +  modules/http2/h2_protocol.c        modules/http2/h2_push.c
>> +  modules/http2/h2_request.c         modules/http2/h2_session.c
>> +  modules/http2/h2_stream.c          modules/http2/h2_switch.c
>> +  modules/http2/h2_util.c            modules/http2/h2_workers.c
>> )
>> SET(mod_ldap_extra_defines           LDAP_DECLARE_EXPORT)
>> SET(mod_ldap_extra_libs              wldap32)
>> 
>> Added: httpd/httpd/trunk/changes-entries/http2_additions.txt
>> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/changes-entries/http2_additions.txt?rev=1894163&view=auto
>> ==============================================================================
>> --- httpd/httpd/trunk/changes-entries/http2_additions.txt (added)
>> +++ httpd/httpd/trunk/changes-entries/http2_additions.txt Tue Oct 12 13:34:01 2021
>> @@ -0,0 +1,17 @@
>> +  *) mod_http2:
>> +     - Fixed an issue since 1.15.24 that "Server" headers in proxied requests
>> +       were overwritten instead of preserved. [PR by @daum3ns]
>> +     - Added directove 'H2StreamTimeout' to configure a separate value for HTTP/2
>> +       streams, overriding server's 'Timeout' configuration. [rpluem]
>> +     - HTTP/2 connections now use pollsets to monitor the status of the
>> +       ongoing streams and their main connection when host OS allows this.
>> +     - Removed work-arounds for older versions of libnghttp2 and checking
>> +       during configure that at least version 1.15.0 is present.
>> +     - The HTTP/2 connection state handler, based on an experiment and draft
>> +       at the IETF http working group (abandoned for some time), has been removed.
>> +     - H2SerializeHeaders no longer has an effect. A warning is logged when it is
>> +       set to "on". The switch enabled the internal writing of requests to be parsed
>> +       by the internal HTTP/1.1 protocol handler and was introduced to avoid
>> +       potential incompatibilities during the introduction of HTTP/2.
>> +     - Removed the abort/redo of tasks when mood swings lower the active limit.
>> +     [Ruediger Pluem, daum3ns, Stefan Eissing]
>> \ No newline at end of file
>> 
>> Modified: httpd/httpd/trunk/modules/http2/NWGNUmod_http2
>> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/NWGNUmod_http2?rev=1894163&r1=1894162&r2=1894163&view=diff
>> ==============================================================================
> 
>> 
>> Modified: httpd/httpd/trunk/modules/http2/h2.h
>> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2.h?rev=1894163&r1=1894162&r2=1894163&view=diff
>> ==============================================================================
>> --- httpd/httpd/trunk/modules/http2/h2.h (original)
>> +++ httpd/httpd/trunk/modules/http2/h2.h Tue Oct 12 13:34:01 2021
> 
>> @@ -138,8 +152,7 @@ struct h2_request {
>>     apr_table_t *headers;
>> 
>>     apr_time_t request_time;
>> -    unsigned int chunked : 1;   /* iff request body needs to be forwarded as chunked */
>> -    unsigned int serialize : 1; /* iff this request is written in HTTP/1.1 serialization */
>> +    int chunked;                /* iff request body needs to be forwarded as chunked */
> 
> Why not leaving it a bitfield although currently only one bit used? Probably we have further flags in the future.

I change that.

>>     apr_off_t raw_bytes;        /* RAW network bytes that generated this request - if known. */
>>     int http_status;            /* Store a possible HTTP status code that gets
>>                                  * defined before creating the dummy HTTP/1.1
> 
>> Modified: httpd/httpd/trunk/modules/http2/h2_bucket_beam.c
>> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_bucket_beam.c?rev=1894163&r1=1894162&r2=1894163&view=diff
>> ==============================================================================
>> --- httpd/httpd/trunk/modules/http2/h2_bucket_beam.c (original)
>> +++ httpd/httpd/trunk/modules/http2/h2_bucket_beam.c Tue Oct 12 13:34:01 2021
> 
>> @@ -191,40 +91,53 @@ static apr_bucket *h2_beam_bucket(h2_buc
>>     return b;
>> }
>> 
>> +static int is_empty(h2_bucket_beam *beam);
>> +static apr_off_t get_buffered_data_len(h2_bucket_beam *beam);
>> 
>> -/*******************************************************************************
>> - * bucket beam that can transport buckets across threads
>> - ******************************************************************************/
>> -
>> -static void mutex_leave(apr_thread_mutex_t *lock)
>> +static int h2_blist_count(h2_blist *blist)
>> {
>> -    apr_thread_mutex_unlock(lock);
>> -}
>> +    apr_bucket *b;
>> +    int count = 0;
>> 
>> -static apr_status_t mutex_enter(void *ctx, h2_beam_lock *pbl)
>> -{
>> -    h2_bucket_beam *beam = ctx;
>> -    pbl->mutex = beam->lock;
>> -    pbl->leave = mutex_leave;
>> -    return apr_thread_mutex_lock(pbl->mutex);
>> +    for (b = H2_BLIST_FIRST(blist); b != H2_BLIST_SENTINEL(blist);
>> +         b = APR_BUCKET_NEXT(b)) {
>> +        ++count;
>> +    }
>> +    return count;
>> }
>> 
>> -static apr_status_t enter_yellow(h2_bucket_beam *beam, h2_beam_lock *pbl)
>> -{
>> -    return mutex_enter(beam, pbl);
>> -}
>> +#define H2_BEAM_LOG(beam, c, level, rv, msg, bb) \
>> +    do { \
>> +        if (APLOG_C_IS_LEVEL((c),(level))) { \
>> +            char buffer[4 * 1024]; \
>> +            apr_size_t len, bmax = sizeof(buffer)/sizeof(buffer[0]); \
>> +            len = bb? h2_util_bb_print(buffer, bmax, "", "", bb) : 0; \
>> +            ap_log_cerror(APLOG_MARK, (level), rv, (c), \
>> +                          "BEAM[%s,%s%sdata=%ld,buckets(send/consumed)=%d/%d]: %s %s", \
>> +                          (beam)->name, \
>> +                          (beam)->aborted? "aborted," : "", \
>> +                          is_empty(beam)? "empty," : "", \
>> +                          (long)get_buffered_data_len(beam), \
>> +                          h2_blist_count(&(beam)->buckets_to_send), \
>> +                          h2_blist_count(&(beam)->buckets_consumed), \
>> +                          (msg), len? buffer : ""); \
>> +        } \
>> +    } while (0)
>> +
>> 
>> -static void leave_yellow(h2_bucket_beam *beam, h2_beam_lock *pbl)
>> +static int bucket_is_mmap(apr_bucket *b)
>> {
>> -    (void)beam;
>> -    if (pbl->leave) {
>> -        pbl->leave(pbl->mutex);
>> -    }
>> +#if APR_HAS_MMAP
>> +    return APR_BUCKET_IS_MMAP(b);
>> +#else
>> +    /* if it is not defined as enabled, it should always be no */
>> +    return 0;
>> +#endif
>> }
>> 
>> static apr_off_t bucket_mem_used(apr_bucket *b)
>> {
>> -    if (APR_BUCKET_IS_FILE(b)) {
>> +    if (APR_BUCKET_IS_FILE(b) || bucket_is_mmap(b)) {
> 
> MMaped buckets also consume physical memory once the content was read. Of course the pages can be dropped from physical memory
> again if space is needed. Furthermore they consume address space in the process, but I admit that this will unlikely cause
> any issues on 64 bit architectures. The only thing that can happen here is that people complain about large virtual memory sizes
> of processes which has no real impact in this case.

My (maybe faulty) reasoning here is: 
HTTP/2 does not create any FILE or MMAP buckets. It just wants to transfer them efficiently from c2 to c1. Instead of reading them and copying the chunks, it apr_mmap_dup() or apr_file_setaside() their content for the receiving bucket brigade. 

This should (as I understand it) not duplicate any memory or use more memory than in a HTTP/1.1 response (plus the usual h2 overhead).

> 
>>         return 0;
>>     }
>>     else {
>> @@ -233,53 +146,37 @@ static apr_off_t bucket_mem_used(apr_buc
>>     }
>> }
>> 
>> -static int report_consumption(h2_bucket_beam *beam, h2_beam_lock *pbl)
>> +static int report_consumption(h2_bucket_beam *beam, int locked)
>> {
>>     int rv = 0;
>> -    apr_off_t len = beam->received_bytes - beam->cons_bytes_reported;
>> +    apr_off_t len = beam->recv_bytes - beam->recv_bytes_reported;
>>     h2_beam_io_callback *cb = beam->cons_io_cb;
>> 
>>     if (len > 0) {
>>         if (cb) {
>>             void *ctx = beam->cons_ctx;
>> 
>> -            if (pbl) leave_yellow(beam, pbl);
>> +            if (locked) apr_thread_mutex_unlock(beam->lock);
>>             cb(ctx, beam, len);
>> -            if (pbl) enter_yellow(beam, pbl);
>> +            if (locked) apr_thread_mutex_lock(beam->lock);
>>             rv = 1;
>>         }
>> -        beam->cons_bytes_reported += len;
>> +        beam->recv_bytes_reported += len;
>>     }
>>     return rv;
>> }
>> 
>> -static void report_prod_io(h2_bucket_beam *beam, int force, h2_beam_lock *pbl)
>> -{
>> -    apr_off_t len = beam->sent_bytes - beam->prod_bytes_reported;
>> -    if (force || len > 0) {
>> -        h2_beam_io_callback *cb = beam->prod_io_cb; 
>> -        if (cb) {
>> -            void *ctx = beam->prod_ctx;
>> -            
>> -            leave_yellow(beam, pbl);
>> -            cb(ctx, beam, len);
>> -            enter_yellow(beam, pbl);
>> -        }
>> -        beam->prod_bytes_reported += len;
>> -    }
>> -}
>> -
>> static apr_size_t calc_buffered(h2_bucket_beam *beam)
>> {
>>     apr_size_t len = 0;
>>     apr_bucket *b;
>> -    for (b = H2_BLIST_FIRST(&beam->send_list); 
>> -         b != H2_BLIST_SENTINEL(&beam->send_list);
>> +    for (b = H2_BLIST_FIRST(&beam->buckets_to_send);
>> +         b != H2_BLIST_SENTINEL(&beam->buckets_to_send);
>>          b = APR_BUCKET_NEXT(b)) {
>>         if (b->length == ((apr_size_t)-1)) {
>>             /* do not count */
>>         }
>> -        else if (APR_BUCKET_IS_FILE(b)) {
>> +        else if (APR_BUCKET_IS_FILE(b) || bucket_is_mmap(b)) {
>>             /* if unread, has no real mem footprint. */
> 
> Same comment as above. No real mem footprint until read, but address space is consumed.
> 
>>         }
>>         else {
> 
> Regards
> 
> Rüdiger
> 


Re: svn commit: r1894163 [1/8] - in /httpd/httpd/trunk: ./ changes-entries/ modules/http2/ test/modules/http2/

Posted by Ruediger Pluem <rp...@apache.org>.

On 10/12/21 3:34 PM, icing@apache.org wrote:
> Author: icing
> Date: Tue Oct 12 13:34:01 2021
> New Revision: 1894163
> 
> URL: http://svn.apache.org/viewvc?rev=1894163&view=rev
> Log:
>   *) mod_http2:
>      - Fixed an issue since 1.15.24 that "Server" headers in proxied requests
>        were overwritten instead of preserved. [PR by @daum3ns]
>      - Added directove 'H2StreamTimeout' to configure a separate value for HTTP/2
>        streams, overriding server's 'Timeout' configuration. [rpluem]
>      - HTTP/2 connections now use pollsets to monitor the status of the
>        ongoing streams and their main connection when host OS allows this.
>      - Removed work-arounds for older versions of libnghttp2 and checking
>        during configure that at least version 1.15.0 is present.
>      - The HTTP/2 connection state handler, based on an experiment and draft
>        at the IETF http working group (abandoned for some time), has been removed.
>      - H2SerializeHeaders no longer has an effect. A warning is logged when it is
>        set to "on". The switch enabled the internal writing of requests to be parsed
>        by the internal HTTP/1.1 protocol handler and was introduced to avoid
>        potential incompatibilities during the introduction of HTTP/2.
>      - Removed the abort/redo of tasks when mood swings lower the active limit.
> 
> 
> Added:
>     httpd/httpd/trunk/changes-entries/http2_additions.txt
>     httpd/httpd/trunk/modules/http2/h2_c1.c
>     httpd/httpd/trunk/modules/http2/h2_c1.h
>     httpd/httpd/trunk/modules/http2/h2_c1_io.c
>     httpd/httpd/trunk/modules/http2/h2_c1_io.h
>     httpd/httpd/trunk/modules/http2/h2_c2.c
>     httpd/httpd/trunk/modules/http2/h2_c2.h
>     httpd/httpd/trunk/modules/http2/h2_c2_filter.c
>     httpd/httpd/trunk/modules/http2/h2_c2_filter.h
>     httpd/httpd/trunk/modules/http2/h2_conn_ctx.c
>     httpd/httpd/trunk/modules/http2/h2_conn_ctx.h
>     httpd/httpd/trunk/modules/http2/h2_protocol.c
>     httpd/httpd/trunk/modules/http2/h2_protocol.h
> Removed:
>     httpd/httpd/trunk/modules/http2/h2_alt_svc.c
>     httpd/httpd/trunk/modules/http2/h2_alt_svc.h
>     httpd/httpd/trunk/modules/http2/h2_conn.c
>     httpd/httpd/trunk/modules/http2/h2_conn.h
>     httpd/httpd/trunk/modules/http2/h2_conn_io.c
>     httpd/httpd/trunk/modules/http2/h2_conn_io.h
>     httpd/httpd/trunk/modules/http2/h2_ctx.c
>     httpd/httpd/trunk/modules/http2/h2_ctx.h
>     httpd/httpd/trunk/modules/http2/h2_filter.c
>     httpd/httpd/trunk/modules/http2/h2_filter.h
>     httpd/httpd/trunk/modules/http2/h2_from_h1.c
>     httpd/httpd/trunk/modules/http2/h2_from_h1.h
>     httpd/httpd/trunk/modules/http2/h2_h2.c
>     httpd/httpd/trunk/modules/http2/h2_h2.h
>     httpd/httpd/trunk/modules/http2/h2_task.c
>     httpd/httpd/trunk/modules/http2/h2_task.h
> Modified:
>     httpd/httpd/trunk/CMakeLists.txt
>     httpd/httpd/trunk/modules/http2/NWGNUmod_http2
>     httpd/httpd/trunk/modules/http2/config2.m4
>     httpd/httpd/trunk/modules/http2/h2.h
>     httpd/httpd/trunk/modules/http2/h2_bucket_beam.c
>     httpd/httpd/trunk/modules/http2/h2_bucket_beam.h
>     httpd/httpd/trunk/modules/http2/h2_config.c
>     httpd/httpd/trunk/modules/http2/h2_config.h
>     httpd/httpd/trunk/modules/http2/h2_headers.c
>     httpd/httpd/trunk/modules/http2/h2_mplx.c
>     httpd/httpd/trunk/modules/http2/h2_mplx.h
>     httpd/httpd/trunk/modules/http2/h2_proxy_session.c
>     httpd/httpd/trunk/modules/http2/h2_proxy_util.c
>     httpd/httpd/trunk/modules/http2/h2_proxy_util.h
>     httpd/httpd/trunk/modules/http2/h2_push.c
>     httpd/httpd/trunk/modules/http2/h2_request.c
>     httpd/httpd/trunk/modules/http2/h2_request.h
>     httpd/httpd/trunk/modules/http2/h2_session.c
>     httpd/httpd/trunk/modules/http2/h2_session.h
>     httpd/httpd/trunk/modules/http2/h2_stream.c
>     httpd/httpd/trunk/modules/http2/h2_stream.h
>     httpd/httpd/trunk/modules/http2/h2_switch.c
>     httpd/httpd/trunk/modules/http2/h2_util.c
>     httpd/httpd/trunk/modules/http2/h2_util.h
>     httpd/httpd/trunk/modules/http2/h2_version.h
>     httpd/httpd/trunk/modules/http2/h2_workers.c
>     httpd/httpd/trunk/modules/http2/h2_workers.h
>     httpd/httpd/trunk/modules/http2/mod_http2.c
>     httpd/httpd/trunk/modules/http2/mod_http2.dsp
>     httpd/httpd/trunk/modules/http2/mod_proxy_http2.c
>     httpd/httpd/trunk/test/modules/http2/test_105_timeout.py
>     httpd/httpd/trunk/test/modules/http2/test_712_buffering.py
> 
> Modified: httpd/httpd/trunk/CMakeLists.txt
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CMakeLists.txt?rev=1894163&r1=1894162&r2=1894163&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/CMakeLists.txt (original)
> +++ httpd/httpd/trunk/CMakeLists.txt Tue Oct 12 13:34:01 2021
> @@ -469,18 +469,15 @@ SET(mod_http2_extra_defines          ssi
>  SET(mod_http2_extra_includes         ${NGHTTP2_INCLUDE_DIR})
>  SET(mod_http2_extra_libs             ${NGHTTP2_LIBRARIES})
>  SET(mod_http2_extra_sources
> -  modules/http2/h2_alt_svc.c
> -  modules/http2/h2_bucket_eos.c      modules/http2/h2_config.c
> -  modules/http2/h2_conn.c            modules/http2/h2_conn_io.c
> -  modules/http2/h2_ctx.c             modules/http2/h2_filter.c
> -  modules/http2/h2_from_h1.c         modules/http2/h2_h2.c
> -  modules/http2/h2_bucket_beam.c
> -  modules/http2/h2_mplx.c            modules/http2/h2_push.c
> -  modules/http2/h2_request.c         modules/http2/h2_headers.c
> -  modules/http2/h2_session.c         modules/http2/h2_stream.c 
> -  modules/http2/h2_switch.c
> -  modules/http2/h2_task.c            modules/http2/h2_util.c
> -  modules/http2/h2_workers.c
> +  modules/http2/h2_bucket_beam.c     modules/http2/h2_bucket_eos.c
> +  modules/http2/h2_c1.c              modules/http2/h2_c1_io.c
> +  modules/http2/h2_c2.c              modules/http2/h2_c2_filter.c
> +  modules/http2/h2_config.c          modules/http2/h2_conn_ctx.c
> +  modules/http2/h2_headers.c         modules/http2/h2_mplx.c
> +  modules/http2/h2_protocol.c        modules/http2/h2_push.c
> +  modules/http2/h2_request.c         modules/http2/h2_session.c
> +  modules/http2/h2_stream.c          modules/http2/h2_switch.c
> +  modules/http2/h2_util.c            modules/http2/h2_workers.c
>  )
>  SET(mod_ldap_extra_defines           LDAP_DECLARE_EXPORT)
>  SET(mod_ldap_extra_libs              wldap32)
> 
> Added: httpd/httpd/trunk/changes-entries/http2_additions.txt
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/changes-entries/http2_additions.txt?rev=1894163&view=auto
> ==============================================================================
> --- httpd/httpd/trunk/changes-entries/http2_additions.txt (added)
> +++ httpd/httpd/trunk/changes-entries/http2_additions.txt Tue Oct 12 13:34:01 2021
> @@ -0,0 +1,17 @@
> +  *) mod_http2:
> +     - Fixed an issue since 1.15.24 that "Server" headers in proxied requests
> +       were overwritten instead of preserved. [PR by @daum3ns]
> +     - Added directove 'H2StreamTimeout' to configure a separate value for HTTP/2
> +       streams, overriding server's 'Timeout' configuration. [rpluem]
> +     - HTTP/2 connections now use pollsets to monitor the status of the
> +       ongoing streams and their main connection when host OS allows this.
> +     - Removed work-arounds for older versions of libnghttp2 and checking
> +       during configure that at least version 1.15.0 is present.
> +     - The HTTP/2 connection state handler, based on an experiment and draft
> +       at the IETF http working group (abandoned for some time), has been removed.
> +     - H2SerializeHeaders no longer has an effect. A warning is logged when it is
> +       set to "on". The switch enabled the internal writing of requests to be parsed
> +       by the internal HTTP/1.1 protocol handler and was introduced to avoid
> +       potential incompatibilities during the introduction of HTTP/2.
> +     - Removed the abort/redo of tasks when mood swings lower the active limit.
> +     [Ruediger Pluem, daum3ns, Stefan Eissing]
> \ No newline at end of file
> 
> Modified: httpd/httpd/trunk/modules/http2/NWGNUmod_http2
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/NWGNUmod_http2?rev=1894163&r1=1894162&r2=1894163&view=diff
> ==============================================================================

> 
> Modified: httpd/httpd/trunk/modules/http2/h2.h
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2.h?rev=1894163&r1=1894162&r2=1894163&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/modules/http2/h2.h (original)
> +++ httpd/httpd/trunk/modules/http2/h2.h Tue Oct 12 13:34:01 2021

> @@ -138,8 +152,7 @@ struct h2_request {
>      apr_table_t *headers;
>  
>      apr_time_t request_time;
> -    unsigned int chunked : 1;   /* iff request body needs to be forwarded as chunked */
> -    unsigned int serialize : 1; /* iff this request is written in HTTP/1.1 serialization */
> +    int chunked;                /* iff request body needs to be forwarded as chunked */

Why not leaving it a bitfield although currently only one bit used? Probably we have further flags in the future.


>      apr_off_t raw_bytes;        /* RAW network bytes that generated this request - if known. */
>      int http_status;            /* Store a possible HTTP status code that gets
>                                   * defined before creating the dummy HTTP/1.1

> Modified: httpd/httpd/trunk/modules/http2/h2_bucket_beam.c
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_bucket_beam.c?rev=1894163&r1=1894162&r2=1894163&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/modules/http2/h2_bucket_beam.c (original)
> +++ httpd/httpd/trunk/modules/http2/h2_bucket_beam.c Tue Oct 12 13:34:01 2021

> @@ -191,40 +91,53 @@ static apr_bucket *h2_beam_bucket(h2_buc
>      return b;
>  }
>  
> +static int is_empty(h2_bucket_beam *beam);
> +static apr_off_t get_buffered_data_len(h2_bucket_beam *beam);
>  
> -/*******************************************************************************
> - * bucket beam that can transport buckets across threads
> - ******************************************************************************/
> -
> -static void mutex_leave(apr_thread_mutex_t *lock)
> +static int h2_blist_count(h2_blist *blist)
>  {
> -    apr_thread_mutex_unlock(lock);
> -}
> +    apr_bucket *b;
> +    int count = 0;
>  
> -static apr_status_t mutex_enter(void *ctx, h2_beam_lock *pbl)
> -{
> -    h2_bucket_beam *beam = ctx;
> -    pbl->mutex = beam->lock;
> -    pbl->leave = mutex_leave;
> -    return apr_thread_mutex_lock(pbl->mutex);
> +    for (b = H2_BLIST_FIRST(blist); b != H2_BLIST_SENTINEL(blist);
> +         b = APR_BUCKET_NEXT(b)) {
> +        ++count;
> +    }
> +    return count;
>  }
>  
> -static apr_status_t enter_yellow(h2_bucket_beam *beam, h2_beam_lock *pbl)
> -{
> -    return mutex_enter(beam, pbl);
> -}
> +#define H2_BEAM_LOG(beam, c, level, rv, msg, bb) \
> +    do { \
> +        if (APLOG_C_IS_LEVEL((c),(level))) { \
> +            char buffer[4 * 1024]; \
> +            apr_size_t len, bmax = sizeof(buffer)/sizeof(buffer[0]); \
> +            len = bb? h2_util_bb_print(buffer, bmax, "", "", bb) : 0; \
> +            ap_log_cerror(APLOG_MARK, (level), rv, (c), \
> +                          "BEAM[%s,%s%sdata=%ld,buckets(send/consumed)=%d/%d]: %s %s", \
> +                          (beam)->name, \
> +                          (beam)->aborted? "aborted," : "", \
> +                          is_empty(beam)? "empty," : "", \
> +                          (long)get_buffered_data_len(beam), \
> +                          h2_blist_count(&(beam)->buckets_to_send), \
> +                          h2_blist_count(&(beam)->buckets_consumed), \
> +                          (msg), len? buffer : ""); \
> +        } \
> +    } while (0)
> +
>  
> -static void leave_yellow(h2_bucket_beam *beam, h2_beam_lock *pbl)
> +static int bucket_is_mmap(apr_bucket *b)
>  {
> -    (void)beam;
> -    if (pbl->leave) {
> -        pbl->leave(pbl->mutex);
> -    }
> +#if APR_HAS_MMAP
> +    return APR_BUCKET_IS_MMAP(b);
> +#else
> +    /* if it is not defined as enabled, it should always be no */
> +    return 0;
> +#endif
>  }
>  
>  static apr_off_t bucket_mem_used(apr_bucket *b)
>  {
> -    if (APR_BUCKET_IS_FILE(b)) {
> +    if (APR_BUCKET_IS_FILE(b) || bucket_is_mmap(b)) {

MMaped buckets also consume physical memory once the content was read. Of course the pages can be dropped from physical memory
again if space is needed. Furthermore they consume address space in the process, but I admit that this will unlikely cause
any issues on 64 bit architectures. The only thing that can happen here is that people complain about large virtual memory sizes
of processes which has no real impact in this case.

>          return 0;
>      }
>      else {
> @@ -233,53 +146,37 @@ static apr_off_t bucket_mem_used(apr_buc
>      }
>  }
>  
> -static int report_consumption(h2_bucket_beam *beam, h2_beam_lock *pbl)
> +static int report_consumption(h2_bucket_beam *beam, int locked)
>  {
>      int rv = 0;
> -    apr_off_t len = beam->received_bytes - beam->cons_bytes_reported;
> +    apr_off_t len = beam->recv_bytes - beam->recv_bytes_reported;
>      h2_beam_io_callback *cb = beam->cons_io_cb;
>       
>      if (len > 0) {
>          if (cb) {
>              void *ctx = beam->cons_ctx;
>              
> -            if (pbl) leave_yellow(beam, pbl);
> +            if (locked) apr_thread_mutex_unlock(beam->lock);
>              cb(ctx, beam, len);
> -            if (pbl) enter_yellow(beam, pbl);
> +            if (locked) apr_thread_mutex_lock(beam->lock);
>              rv = 1;
>          }
> -        beam->cons_bytes_reported += len;
> +        beam->recv_bytes_reported += len;
>      }
>      return rv;
>  }
>  
> -static void report_prod_io(h2_bucket_beam *beam, int force, h2_beam_lock *pbl)
> -{
> -    apr_off_t len = beam->sent_bytes - beam->prod_bytes_reported;
> -    if (force || len > 0) {
> -        h2_beam_io_callback *cb = beam->prod_io_cb; 
> -        if (cb) {
> -            void *ctx = beam->prod_ctx;
> -            
> -            leave_yellow(beam, pbl);
> -            cb(ctx, beam, len);
> -            enter_yellow(beam, pbl);
> -        }
> -        beam->prod_bytes_reported += len;
> -    }
> -}
> -
>  static apr_size_t calc_buffered(h2_bucket_beam *beam)
>  {
>      apr_size_t len = 0;
>      apr_bucket *b;
> -    for (b = H2_BLIST_FIRST(&beam->send_list); 
> -         b != H2_BLIST_SENTINEL(&beam->send_list);
> +    for (b = H2_BLIST_FIRST(&beam->buckets_to_send);
> +         b != H2_BLIST_SENTINEL(&beam->buckets_to_send);
>           b = APR_BUCKET_NEXT(b)) {
>          if (b->length == ((apr_size_t)-1)) {
>              /* do not count */
>          }
> -        else if (APR_BUCKET_IS_FILE(b)) {
> +        else if (APR_BUCKET_IS_FILE(b) || bucket_is_mmap(b)) {
>              /* if unread, has no real mem footprint. */

Same comment as above. No real mem footprint until read, but address space is consumed.

>          }
>          else {

Regards

Rüdiger