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 2023/01/09 07:35:19 UTC

svn commit: r1906475 [2/11] - in /httpd/httpd/branches/2.4.x: ./ changes-entries/ modules/http2/ test/ test/modules/http2/ test/modules/http2/htdocs/cgi/ test/modules/http2/mod_h2test/ test/pyhttpd/ test/pyhttpd/mod_aptest/

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_bucket_beam.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_bucket_beam.h?rev=1906475&r1=1906474&r2=1906475&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_bucket_beam.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_bucket_beam.h Mon Jan  9 07:35:18 2023
@@ -17,190 +17,60 @@
 #ifndef h2_bucket_beam_h
 #define h2_bucket_beam_h
 
+#include "h2_conn_ctx.h"
+
 struct apr_thread_mutex_t;
 struct apr_thread_cond_t;
 
-/*******************************************************************************
- * apr_bucket list without bells and whistles
- ******************************************************************************/
- 
-/**
- * h2_blist can hold a list of buckets just like apr_bucket_brigade, but
- * does not to any allocations or related features.
- */
-typedef struct {
-    APR_RING_HEAD(h2_bucket_list, apr_bucket) list;
-} h2_blist;
-
-#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_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_BLIST_CONCAT(a, b) do {					\
-        APR_RING_CONCAT(&(a)->list, &(b)->list, apr_bucket, link);	\
-    } while (0)
-#define H2_BLIST_PREPEND(a, b) do {					\
-        APR_RING_PREPEND(&(a)->list, &(b)->list, apr_bucket, link);	\
-    } while (0)
-
-/*******************************************************************************
- * h2_bucket_beam
- ******************************************************************************/
-
 /**
  * A h2_bucket_beam solves the task of transferring buckets, esp. their data,
- * across threads with zero buffer copies.
- *
- * When a thread, let's call it the sender thread, wants to send buckets to
- * another, the green thread, it creates a h2_bucket_beam and adds buckets
- * via the h2_beam_send(). It gives the beam to the green thread which then
- * can receive buckets into its own brigade via h2_beam_receive().
- *
- * Sending and receiving can happen concurrently.
- *
- * The beam can limit the amount of data it accepts via the buffer_size. This
- * can also be adjusted during its lifetime. Sends and receives can be done blocking. 
- * A timeout can be set for such blocks.
- *
- * Care needs to be taken when terminating the beam. The beam registers at
- * the pool it was created with and will cleanup after itself. However, if
- * received buckets do still exist, already freed memory might be accessed.
- * The beam does a assertion on this condition.
- * 
- * The proper way of shutting down a beam is to first make sure there are no
- * more green buckets out there, then cleanup the beam to purge eventually
- * still existing sender buckets and then, possibly, terminate the beam itself
- * (or the pool it was created with).
- *
- * The following restrictions apply to bucket transport:
- * - only EOS and FLUSH meta buckets are copied through. All other meta buckets
- *   are kept in the beams hold.
- * - all kind of data buckets are transported through:
- *   - transient buckets are converted to heap ones on send
- *   - heap and pool buckets require no extra handling
- *   - buckets with indeterminate length are read on send
- *   - file buckets will transfer the file itself into a new bucket, if allowed
- *   - all other buckets are read on send to make sure data is present
- *
- * This assures that when the sender thread sends its sender buckets, the data
- * is made accessible while still on the sender side. The sender bucket then enters
- * the beams hold storage.
- * When the green thread calls receive, sender buckets in the hold are wrapped
- * into special beam buckets. Beam buckets on read present the data directly
- * from the internal sender one, but otherwise live on the green side. When a
- * beam bucket gets destroyed, it notifies its beam that the corresponding
- * sender bucket from the hold may be destroyed.
- * Since the destruction of green buckets happens in the green thread, any
- * corresponding sender bucket can not immediately be destroyed, as that would
- * result in race conditions.
- * Instead, the beam transfers such sender buckets from the hold to the purge
- * storage. Next time there is a call from the sender side, the buckets in
- * purge will be deleted.
- *
- * There are callbacks that can be registesender with a beam:
- * - a "consumed" callback that gets called on the sender side with the
- *   amount of data that has been received by the green side. The amount
- *   is a delta from the last callback invocation. The sender side can trigger
- *   these callbacks by calling h2_beam_send() with a NULL brigade.
- * - a "can_beam_file" callback that can prohibit the transfer of file handles
- *   through the beam. This will cause file buckets to be read on send and
- *   its data buffer will then be transports just like a heap bucket would.
- *   When no callback is registered, no restrictions apply and all files are
- *   passed through.
- *   File handles transfersender to the green side will stay there until the
- *   receiving brigade's pool is destroyed/cleared. If the pool lives very
- *   long or if many different files are beamed, the process might run out
- *   of available file handles.
- *
- * The name "beam" of course is inspired by good old transporter
- * technology where humans are kept inside the transporter's memory
- * buffers until the transmission is complete. Star gates use a similar trick.
+ * across threads with as little copying as possible.
  */
 
-typedef void h2_beam_mutex_leave(struct apr_thread_mutex_t *lock);
-
-typedef struct {
-    apr_thread_mutex_t *mutex;
-    h2_beam_mutex_leave *leave;
-} h2_beam_lock;
-
 typedef struct h2_bucket_beam h2_bucket_beam;
 
-typedef apr_status_t h2_beam_mutex_enter(void *ctx, h2_beam_lock *pbl);
-
 typedef void h2_beam_io_callback(void *ctx, h2_bucket_beam *beam,
                                  apr_off_t bytes);
 typedef void h2_beam_ev_callback(void *ctx, h2_bucket_beam *beam);
 
-typedef struct h2_beam_proxy h2_beam_proxy;
-typedef struct {
-    APR_RING_HEAD(h2_beam_proxy_list, h2_beam_proxy) list;
-} h2_bproxy_list;
-
-typedef int h2_beam_can_beam_callback(void *ctx, h2_bucket_beam *beam,
-                                      apr_file_t *file);
-
-typedef enum {
-    H2_BEAM_OWNER_SEND,
-    H2_BEAM_OWNER_RECV
-} h2_beam_owner_t;
-
 /**
- * Will deny all transfer of apr_file_t across the beam and force
- * a data copy instead.
+ * h2_blist can hold a list of buckets just like apr_bucket_brigade, but
+ * does not to any allocations or related features.
  */
-int h2_beam_no_files(void *ctx, h2_bucket_beam *beam, apr_file_t *file);
+typedef struct {
+    APR_RING_HEAD(h2_bucket_list, apr_bucket) list;
+} h2_blist;
 
 struct h2_bucket_beam {
     int id;
-    const char *tag;
+    const char *name;
+    conn_rec *from;
     apr_pool_t *pool;
-    h2_beam_owner_t owner;
-    h2_blist send_list;
-    h2_blist hold_list;
-    h2_blist purge_list;
-    apr_bucket_brigade *recv_buffer;
-    h2_bproxy_list proxies;
-    apr_pool_t *send_pool;
-    apr_pool_t *recv_pool;
-    
+    h2_blist buckets_to_send;
+    h2_blist buckets_consumed;
+
     apr_size_t max_buf_size;
     apr_interval_time_t timeout;
 
-    apr_off_t sent_bytes;     /* amount of bytes send */
-    apr_off_t received_bytes; /* amount of bytes received */
-
-    apr_size_t buckets_sent;  /* # of beam buckets sent */
-    apr_size_t files_beamed;  /* how many file handles have been set aside */
-    
-    unsigned int aborted : 1;
-    unsigned int closed : 1;
-    unsigned int close_sent : 1;
-    unsigned int tx_mem_limits : 1; /* only memory size counts on transfers */
+    int aborted;
+    int closed;
+    int tx_mem_limits; /* only memory size counts on transfers */
+    int copy_files;
 
     struct apr_thread_mutex_t *lock;
     struct apr_thread_cond_t *change;
     
-    apr_off_t cons_bytes_reported;    /* amount of bytes reported as consumed */
-    h2_beam_ev_callback *cons_ev_cb;
-    h2_beam_io_callback *cons_io_cb;
+    h2_beam_ev_callback *was_empty_cb; /* event: beam changed to non-empty in h2_beam_send() */
+    void *was_empty_ctx;
+    h2_beam_ev_callback *recv_cb;      /* event: buckets were transfered in h2_beam_receive() */
+    void *recv_ctx;
+    h2_beam_ev_callback *send_cb;      /* event: buckets were added in h2_beam_send() */
+    void *send_ctx;
+
+    apr_off_t recv_bytes;             /* amount of bytes transferred in h2_beam_receive() */
+    apr_off_t recv_bytes_reported;    /* amount of bytes reported as received via callback */
+    h2_beam_io_callback *cons_io_cb;  /* report: recv_bytes deltas for sender */
     void *cons_ctx;
-
-    apr_off_t prod_bytes_reported;    /* amount of bytes reported as produced */
-    h2_beam_io_callback *prod_io_cb;
-    void *prod_ctx;
-
-    h2_beam_can_beam_callback *can_beam_fn;
-    void *can_beam_ctx;
 };
 
 /**
@@ -211,62 +81,68 @@ struct h2_bucket_beam {
  * that is only used inside that same mutex.
  *
  * @param pbeam         will hold the created beam on return
+ * @param c_from        connection from which buchets are sent
  * @param pool          pool owning the beam, beam will cleanup when pool released
  * @param id            identifier of the beam
  * @param tag           tag identifying beam for logging
- * @param owner         if the beam is owned by the sender or receiver, e.g. if
- *                      the pool owner is using this beam for sending or receiving
  * @param buffer_size   maximum memory footprint of buckets buffered in beam, or
  *                      0 for no limitation
  * @param timeout       timeout for blocking operations
  */
 apr_status_t h2_beam_create(h2_bucket_beam **pbeam,
+                            conn_rec *from,
                             apr_pool_t *pool, 
                             int id, const char *tag,
-                            h2_beam_owner_t owner,  
                             apr_size_t buffer_size,
                             apr_interval_time_t timeout);
 
 /**
  * Destroys the beam immediately without cleanup.
  */ 
-apr_status_t h2_beam_destroy(h2_bucket_beam *beam);
+apr_status_t h2_beam_destroy(h2_bucket_beam *beam, conn_rec *c);
 
 /**
- * Send buckets from the given brigade through the beam. Will hold buckets 
- * internally as long as they have not been processed by the receiving side.
- * All accepted buckets are removed from the given brigade. Will return with
- * APR_EAGAIN on non-blocking sends when not all buckets could be accepted.
- * 
- * Call from the sender side only.
+ * Switch copying of file buckets on/off.
  */
-apr_status_t h2_beam_send(h2_bucket_beam *beam,  
-                          apr_bucket_brigade *bb, 
-                          apr_read_type_e block);
+void h2_beam_set_copy_files(h2_bucket_beam * beam, int enabled);
 
 /**
- * Register the pool from which future buckets are send. This defines
- * the lifetime of the buckets, e.g. the pool should not be cleared/destroyed
- * until the data is no longer needed (or has been received).
+ * Send buckets from the given brigade through the beam.
+ * This can block of the amount of bucket data is above the buffer limit.
+ * @param beam the beam to add buckets to
+ * @param from the connection the sender operates on, must be the same as
+ *             used to create the beam
+ * @param bb the brigade to take buckets from
+ * @param block if the sending should block when the buffer is full
+ * @param pwritten on return, contains the number of data bytes sent
+ * @return APR_SUCCESS when buckets were added to the beam. This can be
+ *                     a partial transfer and other buckets may still remain in bb
+ *         APR_EAGAIN on non-blocking send when the buffer is full
+ *         APR_TIMEUP on blocking semd that time out
+ *         APR_ECONNABORTED when beam has been aborted
  */
-void h2_beam_send_from(h2_bucket_beam *beam, apr_pool_t *p);
+apr_status_t h2_beam_send(h2_bucket_beam *beam, conn_rec *from,
+                          apr_bucket_brigade *bb, 
+                          apr_read_type_e block,
+                          apr_off_t *pwritten);
 
 /**
- * Receive buckets from the beam into the given brigade. Will return APR_EOF
- * when reading past an EOS bucket. Reads can be blocking until data is 
- * available or the beam has been closed. Non-blocking calls return APR_EAGAIN
- * if no data is available.
- *
- * Call from the receiver side only.
- * @param pclosed  on return != 0 iff the beam has been closed by the sender. It
- *                 may still hold untransfered data. Maybe NULL if the caller is
- *                 not interested in this.
+ * Receive buckets from the beam into the given brigade. The caller is
+ * operating on connection `to`.
+ * @param beam the beam to receive buckets from
+ * @param to the connection the receiver is working with
+ * @param bb the bucket brigade to append to
+ * @param block if the read should block when buckets are unavailable
+ * @param readbytes the amount of data the receiver wants
+ * @return APR_SUCCESS when buckets were appended
+ *         APR_EAGAIN on non-blocking read when no buckets are available
+ *         APR_TIMEUP on blocking reads that time out
+ *         APR_ECONNABORTED when beam has been aborted
  */
-apr_status_t h2_beam_receive(h2_bucket_beam *beam, 
-                             apr_bucket_brigade *green_buckets, 
+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);
 
 /**
  * Determine if beam is empty. 
@@ -274,53 +150,27 @@ apr_status_t h2_beam_receive(h2_bucket_b
 int h2_beam_empty(h2_bucket_beam *beam);
 
 /**
- * Determine if beam has handed out proxy buckets that are not destroyed. 
- */
-int h2_beam_holds_proxies(h2_bucket_beam *beam);
-
-/**
- * Abort the beam. Will cleanup any buffered buckets and answer all send
- * and receives with APR_ECONNABORTED.
- * 
- * Call from the sender side only.
- */
-void h2_beam_abort(h2_bucket_beam *beam);
-
-/**
- * Close the beam. Sending an EOS bucket serves the same purpose. 
- * 
- * Call from the sender side only.
- */
-apr_status_t h2_beam_close(h2_bucket_beam *beam);
-
-/**
- * Receives leaves the beam, e.g. will no longer read. This will
- * interrupt any sender blocked writing and fail future send. 
- * 
- * Call from the receiver side only.
+ * Abort the beam, either from receiving or sending side.
+ *
+ * @param beam the beam to abort
+ * @param c the connection the caller is working with
  */
-apr_status_t h2_beam_leave(h2_bucket_beam *beam);
-
-int h2_beam_is_closed(h2_bucket_beam *beam);
+void h2_beam_abort(h2_bucket_beam *beam, conn_rec *c);
 
 /**
- * Return APR_SUCCESS when all buckets in transit have been handled. 
- * When called with APR_BLOCK_READ and a mutex set, will wait until the green
- * side has consumed all data. Otherwise APR_EAGAIN is returned.
- * With clear_buffers set, any queued data is discarded.
- * If a timeout is set on the beam, waiting might also time out and
- * return APR_ETIMEUP.
+ * Close the beam. Make certain an EOS is sent.
  *
- * Call from the sender side only.
+ * @param beam the beam to abort
+ * @param c the connection the caller is working with
  */
-apr_status_t h2_beam_wait_empty(h2_bucket_beam *beam, apr_read_type_e block);
+void h2_beam_close(h2_bucket_beam *beam, conn_rec *c);
 
-/** 
- * Set/get the timeout for blocking read/write operations. Only works
- * if a mutex has been set for the beam.
+/**
+ * Set/get the timeout for blocking sebd/receive operations.
  */
 void h2_beam_timeout_set(h2_bucket_beam *beam, 
                          apr_interval_time_t timeout);
+
 apr_interval_time_t h2_beam_timeout_get(h2_bucket_beam *beam);
 
 /**
@@ -335,7 +185,6 @@ apr_size_t h2_beam_buffer_size_get(h2_bu
  * amount of bytes that have been consumed by the receiver, since the
  * last callback invocation or reset.
  * @param beam the beam to set the callback on
- * @param ev_cb the callback or NULL, called when bytes are consumed
  * @param io_cb the callback or NULL, called on sender with bytes consumed
  * @param ctx  the context to use in callback invocation
  * 
@@ -343,43 +192,48 @@ apr_size_t h2_beam_buffer_size_get(h2_bu
  * from any side.
  */
 void h2_beam_on_consumed(h2_bucket_beam *beam, 
-                         h2_beam_ev_callback *ev_cb,
                          h2_beam_io_callback *io_cb, void *ctx);
 
 /**
- * Call any registered consumed handler, if any changes have happened
- * since the last invocation. 
- * @return !=0 iff a handler has been called
- *
- * Needs to be invoked from the sending side.
+ * Register a callback to be invoked on the receiver side whenever
+ * buckets have been transfered in a h2_beam_receive() call.
+ * @param beam the beam to set the callback on
+ * @param recv_cb the callback or NULL, called when buckets are received
+ * @param ctx  the context to use in callback invocation
  */
-int h2_beam_report_consumption(h2_bucket_beam *beam);
+void h2_beam_on_received(h2_bucket_beam *beam,
+                         h2_beam_ev_callback *recv_cb, void *ctx);
 
 /**
- * Register a callback to be invoked on the receiver side with the
- * amount of bytes that have been produces by the sender, since the
- * last callback invocation or reset.
+ * Register a call back from the sender side to be invoked when send
+ * has added buckets to the beam.
+ * Unregister by passing a NULL on_send_cb.
  * @param beam the beam to set the callback on
- * @param io_cb the callback or NULL, called on receiver with bytes produced
+ * @param on_send_cb the callback to invoke after buckets were added
  * @param ctx  the context to use in callback invocation
- * 
- * Call from the receiver side, callbacks invoked on either side.
  */
-void h2_beam_on_produced(h2_bucket_beam *beam, 
-                         h2_beam_io_callback *io_cb, void *ctx);
+void h2_beam_on_send(h2_bucket_beam *beam,
+                     h2_beam_ev_callback *on_send_cb, void *ctx);
 
 /**
- * Register a callback that may prevent a file from being beam as
- * file handle, forcing the file content to be copied. Then no callback
- * is set (NULL), file handles are transferred directly.
+ * Register a call back from the sender side to be invoked when send
+ * has added to a previously empty beam.
+ * Unregister by passing a NULL was_empty_cb.
  * @param beam the beam to set the callback on
- * @param io_cb the callback or NULL, called on receiver with bytes produced
+ * @param was_empty_cb the callback to invoke on blocked send
  * @param ctx  the context to use in callback invocation
- * 
- * Call from the receiver side, callbacks invoked on either side.
  */
-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);
+
+/**
+ * Call any registered consumed handler, if any changes have happened
+ * since the last invocation. 
+ * @return !=0 iff a handler has been called
+ *
+ * Needs to be invoked from the sending side.
+ */
+int h2_beam_report_consumption(h2_bucket_beam *beam);
 
 /**
  * Get the amount of bytes currently buffered in the beam (unread).
@@ -391,19 +245,4 @@ apr_off_t h2_beam_get_buffered(h2_bucket
  */
 apr_off_t h2_beam_get_mem_used(h2_bucket_beam *beam);
 
-/**
- * Return != 0 iff (some) data from the beam has been received.
- */
-int h2_beam_was_received(h2_bucket_beam *beam);
-
-apr_size_t h2_beam_get_files_beamed(h2_bucket_beam *beam);
-
-typedef apr_bucket *h2_bucket_beamer(h2_bucket_beam *beam, 
-                                     apr_bucket_brigade *dest,
-                                     const apr_bucket *src);
-
-void h2_register_bucket_beamer(h2_bucket_beamer *beamer);
-
-void h2_beam_log(h2_bucket_beam *beam, conn_rec *c, int level, const char *msg);
-
 #endif /* h2_bucket_beam_h */

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_bucket_eos.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_bucket_eos.c?rev=1906475&r1=1906474&r2=1906475&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_bucket_eos.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_bucket_eos.c Mon Jan  9 07:35:18 2023
@@ -21,6 +21,7 @@
 #include <http_core.h>
 #include <http_connection.h>
 #include <http_log.h>
+#include <http_protocol.h>
 
 #include "h2_private.h"
 #include "h2.h"

Added: httpd/httpd/branches/2.4.x/modules/http2/h2_c1.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_c1.c?rev=1906475&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_c1.c (added)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_c1.c Mon Jan  9 07:35:18 2023
@@ -0,0 +1,323 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include <assert.h>
+#include <apr_strings.h>
+
+#include <ap_mpm.h>
+#include <ap_mmn.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+#include <http_log.h>
+#include <http_connection.h>
+#include <http_protocol.h>
+#include <http_request.h>
+#include <http_ssl.h>
+
+#include <mpm_common.h>
+
+#include "h2_private.h"
+#include "h2.h"
+#include "h2_bucket_beam.h"
+#include "h2_config.h"
+#include "h2_conn_ctx.h"
+#include "h2_mplx.h"
+#include "h2_session.h"
+#include "h2_stream.h"
+#include "h2_protocol.h"
+#include "h2_workers.h"
+#include "h2_c1.h"
+#include "h2_version.h"
+#include "h2_util.h"
+
+static struct h2_workers *workers;
+
+static int async_mpm;
+
+APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_in) *h2_c_logio_add_bytes_in;
+APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *h2_c_logio_add_bytes_out;
+
+apr_status_t h2_c1_child_init(apr_pool_t *pool, server_rec *s)
+{
+    apr_status_t status = APR_SUCCESS;
+    int minw, maxw;
+    apr_time_t idle_limit;
+
+    status = ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm);
+    if (status != APR_SUCCESS) {
+        /* some MPMs do not implemnent this */
+        async_mpm = 0;
+        status = APR_SUCCESS;
+    }
+
+    h2_config_init(pool);
+
+    h2_get_workers_config(s, &minw, &maxw, &idle_limit);
+    workers = h2_workers_create(s, pool, maxw, minw, idle_limit);
+ 
+    h2_c_logio_add_bytes_in = APR_RETRIEVE_OPTIONAL_FN(ap_logio_add_bytes_in);
+    h2_c_logio_add_bytes_out = APR_RETRIEVE_OPTIONAL_FN(ap_logio_add_bytes_out);
+
+    return h2_mplx_c1_child_init(pool, s);
+}
+
+void h2_c1_child_stopping(apr_pool_t *pool, int graceful)
+{
+    if (workers) {
+        h2_workers_shutdown(workers, graceful);
+    }
+}
+
+
+apr_status_t h2_c1_setup(conn_rec *c, request_rec *r, server_rec *s)
+{
+    h2_session *session;
+    h2_conn_ctx_t *ctx;
+    apr_status_t rv;
+    
+    if (!workers) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911) 
+                      "workers not initialized");
+        rv = APR_EGENERAL;
+        goto cleanup;
+    }
+
+    rv = h2_session_create(&session, c, r, s, workers);
+    if (APR_SUCCESS != rv) goto cleanup;
+
+    ctx = h2_conn_ctx_get(c);
+    ap_assert(ctx);
+    h2_conn_ctx_assign_session(ctx, session);
+    /* remove the input filter of mod_reqtimeout, now that the connection
+     * is established and we have switched to h2. reqtimeout has supervised
+     * possibly configured handshake timeouts and needs to get out of the way
+     * now since the rest of its state handling assumes http/1.x to take place. */
+    ap_remove_input_filter_byhandle(c->input_filters, "reqtimeout");
+
+cleanup:
+    return rv;
+}
+
+apr_status_t h2_c1_run(conn_rec *c)
+{
+    apr_status_t status;
+    int mpm_state = 0;
+    h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(c);
+    
+    ap_assert(conn_ctx);
+    ap_assert(conn_ctx->session);
+    do {
+        if (c->cs) {
+            c->cs->sense = CONN_SENSE_DEFAULT;
+            c->cs->state = CONN_STATE_HANDLER;
+        }
+    
+        status = h2_session_process(conn_ctx->session, async_mpm);
+        
+        if (APR_STATUS_IS_EOF(status)) {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, 
+                          H2_SSSN_LOG(APLOGNO(03045), conn_ctx->session,
+                          "process, closing conn"));
+            c->keepalive = AP_CONN_CLOSE;
+        }
+        else {
+            c->keepalive = AP_CONN_KEEPALIVE;
+        }
+        
+        if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
+            break;
+        }
+    } while (!async_mpm
+             && c->keepalive == AP_CONN_KEEPALIVE 
+             && mpm_state != AP_MPMQ_STOPPING);
+
+    if (c->cs) {
+        switch (conn_ctx->session->state) {
+            case H2_SESSION_ST_INIT:
+            case H2_SESSION_ST_IDLE:
+            case H2_SESSION_ST_BUSY:
+            case H2_SESSION_ST_WAIT:
+                c->cs->state = CONN_STATE_WRITE_COMPLETION;
+                if (c->cs && !conn_ctx->session->remote.emitted_count) {
+                    /* let the MPM know that we are not done and want
+                     * the Timeout behaviour instead of a KeepAliveTimeout
+                     * See PR 63534. 
+                     */
+                    c->cs->sense = CONN_SENSE_WANT_READ;
+                }
+                break;
+            case H2_SESSION_ST_CLEANUP:
+            case H2_SESSION_ST_DONE:
+            default:
+                c->cs->state = CONN_STATE_LINGER;
+            break;
+        }
+    }
+
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_c1_pre_close(struct h2_conn_ctx_t *ctx, conn_rec *c)
+{
+    h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(c);
+
+    if (conn_ctx && conn_ctx->session) {
+        apr_status_t status = h2_session_pre_close(conn_ctx->session, async_mpm);
+        return (status == APR_SUCCESS)? DONE : status;
+    }
+    return DONE;
+}
+
+int h2_c1_allows_direct(conn_rec *c)
+{
+    if (!c->master) {
+        int is_tls = ap_ssl_conn_is_ssl(c);
+        const char *needed_protocol = is_tls? "h2" : "h2c";
+        int h2_direct = h2_config_cgeti(c, H2_CONF_DIRECT);
+
+        if (h2_direct < 0) {
+            h2_direct = is_tls? 0 : 1;
+        }
+        return (h2_direct && ap_is_allowed_protocol(c, NULL, NULL, needed_protocol));
+    }
+    return 0;
+}
+
+int h2_c1_can_upgrade(request_rec *r)
+{
+    if (!r->connection->master) {
+        int h2_upgrade = h2_config_rgeti(r, H2_CONF_UPGRADE);
+        return h2_upgrade > 0 || (h2_upgrade < 0 && !ap_ssl_conn_is_ssl(r->connection));
+    }
+    return 0;
+}
+
+static int h2_c1_hook_process_connection(conn_rec* c)
+{
+    apr_status_t status;
+    h2_conn_ctx_t *ctx;
+
+    if (c->master) goto declined;
+    ctx = h2_conn_ctx_get(c);
+
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, process_conn");
+    if (!ctx && c->keepalives == 0) {
+        const char *proto = ap_get_protocol(c);
+
+        if (APLOGctrace1(c)) {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, process_conn, "
+                          "new connection using protocol '%s', direct=%d, "
+                          "tls acceptable=%d", proto, h2_c1_allows_direct(c),
+                          h2_protocol_is_acceptable_c1(c, NULL, 1));
+        }
+
+        if (!strcmp(AP_PROTOCOL_HTTP1, proto)
+            && h2_c1_allows_direct(c)
+            && h2_protocol_is_acceptable_c1(c, NULL, 1)) {
+            /* Fresh connection still is on http/1.1 and H2Direct is enabled.
+             * Otherwise connection is in a fully acceptable state.
+             * -> peek at the first 24 incoming bytes
+             */
+            apr_bucket_brigade *temp;
+            char *peek = NULL;
+            apr_size_t peeklen;
+
+            temp = apr_brigade_create(c->pool, c->bucket_alloc);
+            status = ap_get_brigade(c->input_filters, temp,
+                                    AP_MODE_SPECULATIVE, APR_BLOCK_READ, 24);
+
+            if (status != APR_SUCCESS) {
+                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, APLOGNO(03054)
+                              "h2_h2, error reading 24 bytes speculative");
+                apr_brigade_destroy(temp);
+                return DECLINED;
+            }
+
+            apr_brigade_pflatten(temp, &peek, &peeklen, c->pool);
+            if ((peeklen >= 24) && !memcmp(H2_MAGIC_TOKEN, peek, 24)) {
+                ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+                              "h2_h2, direct mode detected");
+                ctx = h2_conn_ctx_create_for_c1(c, c->base_server,
+                                                ap_ssl_conn_is_ssl(c)? "h2" : "h2c");
+            }
+            else if (APLOGctrace2(c)) {
+                ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+                              "h2_h2, not detected in %d bytes(base64): %s",
+                              (int)peeklen, h2_util_base64url_encode(peek, peeklen, c->pool));
+            }
+            apr_brigade_destroy(temp);
+        }
+    }
+
+    if (!ctx) goto declined;
+
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "process_conn");
+    if (!ctx->session) {
+        status = h2_c1_setup(c, NULL, ctx->server? ctx->server : c->base_server);
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, c, "conn_setup");
+        if (status != APR_SUCCESS) {
+            h2_conn_ctx_detach(c);
+            return !OK;
+        }
+    }
+    h2_c1_run(c);
+    return OK;
+
+declined:
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, declined");
+    return DECLINED;
+}
+
+static int h2_c1_hook_pre_close(conn_rec *c)
+{
+    h2_conn_ctx_t *ctx;
+
+    /* secondary connection? */
+    if (c->master) {
+        return DECLINED;
+    }
+
+    ctx = h2_conn_ctx_get(c);
+    if (ctx) {
+        /* If the session has been closed correctly already, we will not
+         * find a h2_conn_ctx_there. The presence indicates that the session
+         * is still ongoing. */
+        return h2_c1_pre_close(ctx, c);
+    }
+    return DECLINED;
+}
+
+static const char* const mod_ssl[]        = { "mod_ssl.c", NULL};
+static const char* const mod_reqtimeout[] = { "mod_ssl.c", "mod_reqtimeout.c", NULL};
+
+void h2_c1_register_hooks(void)
+{
+    /* Our main processing needs to run quite late. Definitely after mod_ssl,
+     * as we need its connection filters, but also before reqtimeout as its
+     * method of timeouts is specific to HTTP/1.1 (as of now).
+     * The core HTTP/1 processing run as REALLY_LAST, so we will have
+     * a chance to take over before it.
+     */
+    ap_hook_process_connection(h2_c1_hook_process_connection,
+                               mod_reqtimeout, NULL, APR_HOOK_LAST);
+
+    /* One last chance to properly say goodbye if we have not done so
+     * already. */
+    ap_hook_pre_close_connection(h2_c1_hook_pre_close, NULL, mod_ssl, APR_HOOK_LAST);
+}
+

Added: httpd/httpd/branches/2.4.x/modules/http2/h2_c1.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_c1.h?rev=1906475&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_c1.h (added)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_c1.h Mon Jan  9 07:35:18 2023
@@ -0,0 +1,83 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_c1__
+#define __mod_h2__h2_c1__
+
+#include <http_core.h>
+
+struct h2_conn_ctx_t;
+
+extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_in) *h2_c_logio_add_bytes_in;
+extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *h2_c_logio_add_bytes_out;
+
+/* Initialize this child process for h2 primary connection work,
+ * to be called once during child init before multi processing
+ * starts.
+ */
+apr_status_t h2_c1_child_init(apr_pool_t *pool, server_rec *s);
+
+/**
+ * Setup the primary connection and our context for HTTP/2 processing
+ *
+ * @param c the connection HTTP/2 is starting on
+ * @param r the upgrade request that still awaits an answer, optional
+ * @param s the server selected for this connection (can be != c->base_server)
+ */
+apr_status_t h2_c1_setup(conn_rec *c, request_rec *r, server_rec *s);
+
+/**
+ * Run the HTTP/2 primary connection in synchronous fashion.
+ * Return when the HTTP/2 session is done
+ * and the connection will close or a fatal error occurred.
+ *
+ * @param c the http2 connection to run
+ * @return APR_SUCCESS when session is done.
+ */
+apr_status_t h2_c1_run(conn_rec *c);
+
+/**
+ * The primary connection is about to close. If we have not send a GOAWAY
+ * yet, this is the last chance.
+ */
+apr_status_t h2_c1_pre_close(struct h2_conn_ctx_t *ctx, conn_rec *c);
+
+/**
+ * Check if the connection allows a direct detection of HTTPP/2,
+ * as configurable by the H2Direct directive.
+ * @param c the connection to check on
+ * @return != 0 if direct detection is enabled
+ */
+int h2_c1_allows_direct(conn_rec *c);
+
+/**
+ * Check if the "Upgrade" HTTP/1.1 mode of protocol switching is enabled
+ * for the given request.
+ * @param r the request to check
+ * @return != 0 iff Upgrade switching is enabled
+ */
+int h2_c1_can_upgrade(request_rec *r);
+
+/* Register hooks for h2 handling on primary connections.
+ */
+void h2_c1_register_hooks(void);
+
+/**
+ * Child is about to be stopped, release unused resources
+ */
+void h2_c1_child_stopping(apr_pool_t *pool, int graceful);
+
+#endif /* defined(__mod_h2__h2_c1__) */

Added: httpd/httpd/branches/2.4.x/modules/http2/h2_c1_io.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_c1_io.c?rev=1906475&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_c1_io.c (added)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_c1_io.c Mon Jan  9 07:35:18 2023
@@ -0,0 +1,545 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+#include <assert.h>
+#include <apr_strings.h>
+#include <ap_mpm.h>
+#include <mpm_common.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_connection.h>
+#include <http_protocol.h>
+#include <http_request.h>
+#include <http_ssl.h>
+
+#include "h2_private.h"
+#include "h2_bucket_eos.h"
+#include "h2_config.h"
+#include "h2_c1.h"
+#include "h2_c1_io.h"
+#include "h2_protocol.h"
+#include "h2_session.h"
+#include "h2_util.h"
+
+#define TLS_DATA_MAX          (16*1024) 
+
+/* Calculated like this: assuming MTU 1500 bytes
+ * 1500 - 40 (IP) - 20 (TCP) - 40 (TCP options) 
+ *      - TLS overhead (60-100) 
+ * ~= 1300 bytes */
+#define WRITE_SIZE_INITIAL    1300
+
+/* The maximum we'd like to write in one chunk is
+ * the max size of a TLS record. When pushing
+ * many frames down the h2 connection, this might
+ * align differently because of headers and other
+ * frames or simply as not sufficient data is
+ * in a response body.
+ * However keeping frames at or below this limit
+ * should make optimizations at the layer that writes
+ * to TLS easier.
+ */
+#define WRITE_SIZE_MAX        (TLS_DATA_MAX) 
+
+#define BUF_REMAIN            ((apr_size_t)(bmax-off))
+
+static void h2_c1_io_bb_log(conn_rec *c, int stream_id, int level,
+                            const char *tag, apr_bucket_brigade *bb)
+{
+    char buffer[16 * 1024];
+    const char *line = "(null)";
+    int bmax = sizeof(buffer)/sizeof(buffer[0]);
+    int off = 0;
+    apr_bucket *b;
+    
+    (void)stream_id;
+    if (bb) {
+        memset(buffer, 0, bmax--);
+        for (b = APR_BRIGADE_FIRST(bb); 
+             bmax && (b != APR_BRIGADE_SENTINEL(bb));
+             b = APR_BUCKET_NEXT(b)) {
+            
+            if (APR_BUCKET_IS_METADATA(b)) {
+                if (APR_BUCKET_IS_EOS(b)) {
+                    off += apr_snprintf(buffer+off, BUF_REMAIN, "eos ");
+                }
+                else if (APR_BUCKET_IS_FLUSH(b)) {
+                    off += apr_snprintf(buffer+off, BUF_REMAIN, "flush ");
+                }
+                else if (AP_BUCKET_IS_EOR(b)) {
+                    off += apr_snprintf(buffer+off, BUF_REMAIN, "eor ");
+                }
+                else if (H2_BUCKET_IS_H2EOS(b)) {
+                    off += apr_snprintf(buffer+off, BUF_REMAIN, "h2eos ");
+                }
+                else {
+                    off += apr_snprintf(buffer+off, BUF_REMAIN, "meta(unknown) ");
+                }
+            }
+            else {
+                const char *btype = "data";
+                if (APR_BUCKET_IS_FILE(b)) {
+                    btype = "file";
+                }
+                else if (APR_BUCKET_IS_PIPE(b)) {
+                    btype = "pipe";
+                }
+                else if (APR_BUCKET_IS_SOCKET(b)) {
+                    btype = "socket";
+                }
+                else if (APR_BUCKET_IS_HEAP(b)) {
+                    btype = "heap";
+                }
+                else if (APR_BUCKET_IS_TRANSIENT(b)) {
+                    btype = "transient";
+                }
+                else if (APR_BUCKET_IS_IMMORTAL(b)) {
+                    btype = "immortal";
+                }
+#if APR_HAS_MMAP
+                else if (APR_BUCKET_IS_MMAP(b)) {
+                    btype = "mmap";
+                }
+#endif
+                else if (APR_BUCKET_IS_POOL(b)) {
+                    btype = "pool";
+                }
+                
+                off += apr_snprintf(buffer+off, BUF_REMAIN, "%s[%ld] ", 
+                                    btype, 
+                                    (long)(b->length == ((apr_size_t)-1)? -1UL : b->length));
+            }
+        }
+        line = *buffer? buffer : "(empty)";
+    }
+    /* Intentional no APLOGNO */
+    ap_log_cerror(APLOG_MARK, level, 0, c, "h2_session(%ld)-%s: %s", 
+                  c->id, tag, line);
+
+}
+#define C1_IO_BB_LOG(c, stream_id, level, tag, bb) \
+    if (APLOG_C_IS_LEVEL(c, level)) { \
+        h2_c1_io_bb_log((c), (stream_id), (level), (tag), (bb)); \
+    }
+
+
+apr_status_t h2_c1_io_init(h2_c1_io *io, h2_session *session)
+{
+    conn_rec *c = session->c1;
+
+    io->session = session;
+    io->output = apr_brigade_create(c->pool, c->bucket_alloc);
+    io->is_tls = ap_ssl_conn_is_ssl(session->c1);
+    io->buffer_output  = io->is_tls;
+    io->flush_threshold = 4 * (apr_size_t)h2_config_sgeti64(session->s, H2_CONF_STREAM_MAX_MEM);
+
+    if (io->buffer_output) {
+        /* This is what we start with, 
+         * see https://issues.apache.org/jira/browse/TS-2503 
+         */
+        io->warmup_size = h2_config_sgeti64(session->s, H2_CONF_TLS_WARMUP_SIZE);
+        io->cooldown_usecs = (h2_config_sgeti(session->s, H2_CONF_TLS_COOLDOWN_SECS)
+                              * APR_USEC_PER_SEC);
+        io->cooldown_usecs = 0;
+        io->write_size = (io->cooldown_usecs > 0?
+                          WRITE_SIZE_INITIAL : WRITE_SIZE_MAX);
+    }
+    else {
+        io->warmup_size = 0;
+        io->cooldown_usecs = 0;
+        io->write_size = 0;
+    }
+
+    if (APLOGctrace1(c)) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, c,
+                      "h2_c1_io(%ld): init, buffering=%d, warmup_size=%ld, "
+                      "cd_secs=%f", c->id, io->buffer_output,
+                      (long)io->warmup_size,
+                      ((double)io->cooldown_usecs/APR_USEC_PER_SEC));
+    }
+
+    return APR_SUCCESS;
+}
+
+static void append_scratch(h2_c1_io *io)
+{
+    if (io->scratch && io->slen > 0) {
+        apr_bucket *b = apr_bucket_heap_create(io->scratch, io->slen,
+                                               apr_bucket_free,
+                                               io->session->c1->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(io->output, b);
+        io->buffered_len += io->slen;
+        io->scratch = NULL;
+        io->slen = io->ssize = 0;
+    }
+}
+
+static apr_size_t assure_scratch_space(h2_c1_io *io) {
+    apr_size_t remain = io->ssize - io->slen; 
+    if (io->scratch && remain == 0) {
+        append_scratch(io);
+    }
+    if (!io->scratch) {
+        /* we control the size and it is larger than what buckets usually
+         * allocate. */
+        io->scratch = apr_bucket_alloc(io->write_size, io->session->c1->bucket_alloc);
+        io->ssize = io->write_size;
+        io->slen = 0;
+        remain = io->ssize;
+    }
+    return remain;
+}
+    
+static apr_status_t read_to_scratch(h2_c1_io *io, apr_bucket *b)
+{
+    apr_status_t status;
+    const char *data;
+    apr_size_t len;
+    
+    if (!b->length) {
+        return APR_SUCCESS;
+    }
+    
+    ap_assert(b->length <= (io->ssize - io->slen));
+    if (APR_BUCKET_IS_FILE(b)) {
+        apr_bucket_file *f = (apr_bucket_file *)b->data;
+        apr_file_t *fd = f->fd;
+        apr_off_t offset = b->start;
+        
+        len = b->length;
+        /* file buckets will read 8000 byte chunks and split
+         * themselves. However, we do know *exactly* how many
+         * bytes we need where. So we read the file directly to
+         * where we need it.
+         */
+        status = apr_file_seek(fd, APR_SET, &offset);
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+        status = apr_file_read(fd, io->scratch + io->slen, &len);
+        if (status != APR_SUCCESS && status != APR_EOF) {
+            return status;
+        }
+        io->slen += len;
+    }
+    else if (APR_BUCKET_IS_MMAP(b)) {
+        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, io->session->c1,
+                      "h2_c1_io(%ld): seeing mmap bucket of size %ld, scratch remain=%ld",
+                      io->session->c1->id, (long)b->length, (long)(io->ssize - io->slen));
+        status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
+        if (status == APR_SUCCESS) {
+            memcpy(io->scratch+io->slen, data, len);
+            io->slen += len;
+        }
+    }
+    else {
+        status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
+        if (status == APR_SUCCESS) {
+            memcpy(io->scratch+io->slen, data, len);
+            io->slen += len;
+        }
+    }
+    return status;
+}
+
+static apr_status_t pass_output(h2_c1_io *io, int flush)
+{
+    conn_rec *c = io->session->c1;
+    apr_off_t bblen;
+    apr_status_t rv;
+    
+    append_scratch(io);
+    if (flush) {
+        if (!APR_BUCKET_IS_FLUSH(APR_BRIGADE_LAST(io->output))) {
+            apr_bucket *b = apr_bucket_flush_create(c->bucket_alloc);
+            APR_BRIGADE_INSERT_TAIL(io->output, b);
+        }
+    }
+    if (APR_BRIGADE_EMPTY(io->output)) {
+        return APR_SUCCESS;
+    }
+    
+    io->unflushed = !APR_BUCKET_IS_FLUSH(APR_BRIGADE_LAST(io->output));
+    apr_brigade_length(io->output, 0, &bblen);
+    C1_IO_BB_LOG(c, 0, APLOG_TRACE2, "out", io->output);
+    
+    rv = ap_pass_brigade(c->output_filters, io->output);
+    if (APR_SUCCESS != rv) goto cleanup;
+
+    io->buffered_len = 0;
+    io->bytes_written += (apr_size_t)bblen;
+
+    if (io->write_size < WRITE_SIZE_MAX
+         && io->bytes_written >= io->warmup_size) {
+        /* connection is hot, use max size */
+        io->write_size = WRITE_SIZE_MAX;
+    }
+    else if (io->cooldown_usecs > 0
+             && io->write_size > WRITE_SIZE_INITIAL) {
+        apr_time_t now = apr_time_now();
+        if ((now - io->last_write) >= io->cooldown_usecs) {
+            /* long time not written, reset write size */
+            io->write_size = WRITE_SIZE_INITIAL;
+            io->bytes_written = 0;
+        }
+        else {
+            io->last_write = now;
+        }
+    }
+
+cleanup:
+    if (APR_SUCCESS != rv) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(03044)
+                      "h2_c1_io(%ld): pass_out brigade %ld bytes",
+                      c->id, (long)bblen);
+    }
+    apr_brigade_cleanup(io->output);
+    return rv;
+}
+
+int h2_c1_io_needs_flush(h2_c1_io *io)
+{
+    return io->buffered_len >= io->flush_threshold;
+}
+
+int h2_c1_io_pending(h2_c1_io *io)
+{
+    return !APR_BRIGADE_EMPTY(io->output) || (io->scratch && io->slen > 0);
+}
+
+apr_status_t h2_c1_io_pass(h2_c1_io *io)
+{
+    apr_status_t rv = APR_SUCCESS;
+
+    if (h2_c1_io_pending(io)) {
+        rv = pass_output(io, 0);
+    }
+    return rv;
+}
+
+apr_status_t h2_c1_io_assure_flushed(h2_c1_io *io)
+{
+    apr_status_t rv = APR_SUCCESS;
+
+    if (h2_c1_io_pending(io) || io->unflushed) {
+        rv = pass_output(io, 1);
+        if (APR_SUCCESS != rv) goto cleanup;
+    }
+cleanup:
+    return rv;
+}
+
+apr_status_t h2_c1_io_add_data(h2_c1_io *io, const char *data, size_t length)
+{
+    apr_status_t status = APR_SUCCESS;
+    apr_size_t remain;
+    
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, io->session->c1,
+                  "h2_c1_io(%ld): adding %ld data bytes",
+                  io->session->c1->id, (long)length);
+    if (io->buffer_output) {
+        while (length > 0) {
+            remain = assure_scratch_space(io);
+            if (remain >= length) {
+                memcpy(io->scratch + io->slen, data, length);
+                io->slen += length;
+                length = 0;
+            }
+            else {
+                memcpy(io->scratch + io->slen, data, remain);
+                io->slen += remain;
+                data += remain;
+                length -= remain;
+            }
+        }
+    }
+    else {
+        status = apr_brigade_write(io->output, NULL, NULL, data, length);
+        io->buffered_len += length;
+    }
+    return status;
+}
+
+apr_status_t h2_c1_io_append(h2_c1_io *io, apr_bucket_brigade *bb)
+{
+    apr_bucket *b;
+    apr_status_t rv = APR_SUCCESS;
+
+    while (!APR_BRIGADE_EMPTY(bb)) {
+        b = APR_BRIGADE_FIRST(bb);
+        if (APR_BUCKET_IS_METADATA(b) || APR_BUCKET_IS_MMAP(b)) {
+            /* need to finish any open scratch bucket, as meta data
+             * needs to be forward "in order". */
+            append_scratch(io);
+            APR_BUCKET_REMOVE(b);
+            APR_BRIGADE_INSERT_TAIL(io->output, b);
+        }
+        else if (io->buffer_output) {
+            apr_size_t remain = assure_scratch_space(io);
+            if (b->length > remain) {
+                apr_bucket_split(b, remain);
+                if (io->slen == 0) {
+                    /* complete write_size bucket, append unchanged */
+                    APR_BUCKET_REMOVE(b);
+                    APR_BRIGADE_INSERT_TAIL(io->output, b);
+                    io->buffered_len += b->length;
+                    continue;
+                }
+            }
+            else {
+                /* bucket fits in remain, copy to scratch */
+                rv = read_to_scratch(io, b);
+                apr_bucket_delete(b);
+                if (APR_SUCCESS != rv) goto cleanup;
+                continue;
+            }
+        }
+        else {
+            /* no buffering, forward buckets setaside on flush */
+            apr_bucket_setaside(b, io->session->c1->pool);
+            APR_BUCKET_REMOVE(b);
+            APR_BRIGADE_INSERT_TAIL(io->output, b);
+            io->buffered_len += b->length;
+        }
+    }
+cleanup:
+    return rv;
+}
+
+static apr_status_t c1_in_feed_bucket(h2_session *session,
+                                      apr_bucket *b, apr_ssize_t *inout_len)
+{
+    apr_status_t rv = APR_SUCCESS;
+    apr_size_t len;
+    const char *data;
+    ssize_t n;
+
+    rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
+    while (APR_SUCCESS == rv && len > 0) {
+        n = nghttp2_session_mem_recv(session->ngh2, (const uint8_t *)data, len);
+
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, session->c1,
+                      H2_SSSN_MSG(session, "fed %ld bytes to nghttp2, %ld read"),
+                      (long)len, (long)n);
+        if (n < 0) {
+            if (nghttp2_is_fatal((int)n)) {
+                h2_session_event(session, H2_SESSION_EV_PROTO_ERROR,
+                                 (int)n, nghttp2_strerror((int)n));
+                rv = APR_EGENERAL;
+            }
+        }
+        else {
+            *inout_len += n;
+            if ((apr_ssize_t)len <= n) {
+                break;
+            }
+            len -= (apr_size_t)n;
+            data += n;
+        }
+    }
+
+    return rv;
+}
+
+static apr_status_t c1_in_feed_brigade(h2_session *session,
+                                       apr_bucket_brigade *bb,
+                                       apr_ssize_t *inout_len)
+{
+    apr_status_t rv = APR_SUCCESS;
+    apr_bucket* b;
+
+    *inout_len = 0;
+    while (!APR_BRIGADE_EMPTY(bb)) {
+        b = APR_BRIGADE_FIRST(bb);
+        if (!APR_BUCKET_IS_METADATA(b)) {
+            rv = c1_in_feed_bucket(session, b, inout_len);
+            if (APR_SUCCESS != rv) goto cleanup;
+        }
+        apr_bucket_delete(b);
+    }
+cleanup:
+    apr_brigade_cleanup(bb);
+    return rv;
+}
+
+static apr_status_t read_and_feed(h2_session *session)
+{
+    apr_ssize_t bytes_fed, bytes_requested;
+    apr_status_t rv;
+
+    bytes_requested = H2MAX(APR_BUCKET_BUFF_SIZE, session->max_stream_mem * 4);
+    rv = ap_get_brigade(session->c1->input_filters,
+                        session->bbtmp, AP_MODE_READBYTES,
+                        APR_NONBLOCK_READ, bytes_requested);
+
+    if (APR_SUCCESS == rv) {
+        if (!APR_BRIGADE_EMPTY(session->bbtmp)) {
+            h2_util_bb_log(session->c1, session->id, APLOG_TRACE2, "c1 in",
+                           session->bbtmp);
+            rv = c1_in_feed_brigade(session, session->bbtmp, &bytes_fed);
+            session->io.bytes_read += bytes_fed;
+        }
+        else {
+            rv = APR_EAGAIN;
+        }
+    }
+    return rv;
+}
+
+apr_status_t h2_c1_read(h2_session *session)
+{
+    apr_status_t rv;
+
+    /* H2_IN filter handles all incoming data against the session.
+     * We just pull at the filter chain to make it happen */
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c1,
+                  H2_SSSN_MSG(session, "session_read start"));
+    rv = read_and_feed(session);
+
+    if (APR_SUCCESS == rv) {
+        h2_session_dispatch_event(session, H2_SESSION_EV_INPUT_PENDING, 0, NULL);
+    }
+    else if (APR_STATUS_IS_EAGAIN(rv)) {
+        /* Signal that we have exhausted the input momentarily.
+         * This might switch to polling the socket */
+        h2_session_dispatch_event(session, H2_SESSION_EV_INPUT_EXHAUSTED, 0, NULL);
+    }
+    else if (APR_SUCCESS != rv) {
+        if (APR_STATUS_IS_ETIMEDOUT(rv)
+            || APR_STATUS_IS_ECONNABORTED(rv)
+            || APR_STATUS_IS_ECONNRESET(rv)
+            || APR_STATUS_IS_EOF(rv)
+            || APR_STATUS_IS_EBADF(rv)) {
+            /* common status for a client that has left */
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, session->c1,
+                          H2_SSSN_MSG(session, "input gone"));
+        }
+        else {
+            /* uncommon status, log on INFO so that we see this */
+            ap_log_cerror( APLOG_MARK, APLOG_DEBUG, rv, session->c1,
+                          H2_SSSN_LOG(APLOGNO(02950), session,
+                          "error reading, terminating"));
+        }
+        h2_session_dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
+    }
+
+    apr_brigade_cleanup(session->bbtmp);
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, session->c1,
+                  H2_SSSN_MSG(session, "session_read done"));
+    return rv;
+}

Added: httpd/httpd/branches/2.4.x/modules/http2/h2_c1_io.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_c1_io.h?rev=1906475&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_c1_io.h (added)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_c1_io.h Mon Jan  9 07:35:18 2023
@@ -0,0 +1,100 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_c1_io__
+#define __mod_h2__h2_c1_io__
+
+struct h2_config;
+struct h2_session;
+
+/* h2_io is the basic handler of a httpd connection. It keeps two brigades,
+ * one for input, one for output and works with the installed connection
+ * filters.
+ * The read is done via a callback function, so that input can be processed
+ * directly without copying.
+ */
+typedef struct {
+    struct h2_session *session;
+    apr_bucket_brigade *output;
+
+    int is_tls;
+    int unflushed;
+    apr_time_t cooldown_usecs;
+    apr_int64_t warmup_size;
+    
+    apr_size_t write_size;
+    apr_time_t last_write;
+    apr_int64_t bytes_read;
+    apr_int64_t bytes_written;
+    
+    int buffer_output;
+    apr_off_t buffered_len;
+    apr_off_t flush_threshold;
+    unsigned int is_flushed : 1;
+    
+    char *scratch;
+    apr_size_t ssize;
+    apr_size_t slen;
+} h2_c1_io;
+
+apr_status_t h2_c1_io_init(h2_c1_io *io, struct h2_session *session);
+
+/**
+ * Append data to the buffered output.
+ * @param buf the data to append
+ * @param length the length of the data to append
+ */
+apr_status_t h2_c1_io_add_data(h2_c1_io *io,
+                         const char *buf,
+                         size_t length);
+
+apr_status_t h2_c1_io_add(h2_c1_io *io, apr_bucket *b);
+
+apr_status_t h2_c1_io_append(h2_c1_io *io, apr_bucket_brigade *bb);
+
+/**
+ * Pass any buffered data on to the connection output filters.
+ * @param io the connection io
+ */
+apr_status_t h2_c1_io_pass(h2_c1_io *io);
+
+/**
+ * if there is any data pendiong or was any data send
+ * since the last FLUSH, send out a FLUSH now.
+ */
+apr_status_t h2_c1_io_assure_flushed(h2_c1_io *io);
+
+/**
+ * Check if the buffered amount of data needs flushing.
+ */
+int h2_c1_io_needs_flush(h2_c1_io *io);
+
+/**
+ * Check if we have output pending.
+ */
+int h2_c1_io_pending(h2_c1_io *io);
+
+struct h2_session;
+
+/**
+ * Read c1 input and pass it on to nghttp2.
+ * @param session the session
+ * @param when_pending != 0 if only pending input (sitting in filters)
+ *                     needs to be read
+ */
+apr_status_t h2_c1_read(struct h2_session *session);
+
+#endif /* defined(__mod_h2__h2_c1_io__) */