mod_htt2, synch with changes from github module version:

- logio: improvements to reporting of sent bytes for http2 responses
  - directive H2OutputBuffering, controls if any output should be sent immediately.



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1886792 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stefan Eissing
2021-02-22 14:11:09 +00:00
parent 42f33b4171
commit 309e083893
15 changed files with 154 additions and 28 deletions

View File

@ -0,0 +1,6 @@
*) mod_http2: Fixed reporting of transferred bytes for mod_logio for
modifiers %O (and %S) to report the number of transferred header and
body lengths. This is still only an approximation of the bytes on the
connection. The data is subject to header compression and h2 framing
afterwards. [Stefan Eissing]

View File

@ -0,0 +1,5 @@
*) mod_http2: new option 'H2OutputBuffering on/off' which controls the
buffering of stream output. The default is on, which is the behaviour of
previous mod-h2 versions. When off, all bytes are made available immediately
to the main connection for sending them out to the client. This fixes interop
issues with certain flavours of gRPC. [Stefan Eissing]

View File

@ -983,4 +983,26 @@ H2TLSCoolDownSecs 0
</p>
</usage>
</directivesynopsis>
<directivesynopsis>
<name>H2OutputBuffering</name>
<description>Determine buffering behaviour of output</description>
<syntax>H2OutputBuffering on/off</syntax>
<default>H2OutputBuffering on</default>
<contextlist>
<context>server config</context>
<context>virtual host</context>
</contextlist>
<compatibility>Available in version 2.4.48 and later.</compatibility>
<usage>
<p>
The option 'H2OutputBuffering on/off' controls the buffering of stream output.
The default is on, which is the behaviour of previous versions. When off, all
bytes are made available immediately to the main connection for sending them
out to the client. This fixes interop issues with certain flavours of gRPC.
</p>
</usage>
</directivesynopsis>
</modulesynopsis>

View File

@ -1042,6 +1042,7 @@ transfer:
H2_BLIST_INSERT_TAIL(&beam->hold_list, bsender);
remain -= bsender->length;
beam->received_bytes += bsender->length;
++transferred;
++transferred_buckets;
continue;

View File

@ -78,6 +78,7 @@ typedef struct h2_config {
int early_hints; /* support status code 103 */
int padding_bits;
int padding_always;
int output_buffered;
} h2_config;
typedef struct h2_dir_config {
@ -115,6 +116,7 @@ static h2_config defconf = {
0, /* early hints, http status 103 */
0, /* padding bits */
1, /* padding always */
1, /* strean output buffered */
};
static h2_dir_config defdconf = {
@ -159,6 +161,7 @@ void *h2_config_create_svr(apr_pool_t *pool, server_rec *s)
conf->early_hints = DEF_VAL;
conf->padding_bits = DEF_VAL;
conf->padding_always = DEF_VAL;
conf->output_buffered = DEF_VAL;
return conf;
}
@ -193,6 +196,7 @@ static void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv)
}
n->push_diary_size = H2_CONFIG_GET(add, base, push_diary_size);
n->copy_files = H2_CONFIG_GET(add, base, copy_files);
n->output_buffered = H2_CONFIG_GET(add, base, output_buffered);
if (add->push_list && base->push_list) {
n->push_list = apr_array_append(pool, base->push_list, add->push_list);
}
@ -286,6 +290,8 @@ static apr_int64_t h2_srv_config_geti64(const h2_config *conf, h2_config_var_t v
return H2_CONFIG_GET(conf, &defconf, padding_bits);
case H2_CONF_PADDING_ALWAYS:
return H2_CONFIG_GET(conf, &defconf, padding_always);
case H2_CONF_OUTPUT_BUFFER:
return H2_CONFIG_GET(conf, &defconf, output_buffered);
default:
return DEF_VAL;
}
@ -351,6 +357,9 @@ static void h2_srv_config_seti(h2_config *conf, h2_config_var_t var, int val)
case H2_CONF_PADDING_ALWAYS:
H2_CONFIG_SET(conf, padding_always, val);
break;
case H2_CONF_OUTPUT_BUFFER:
H2_CONFIG_SET(conf, output_buffered, val);
break;
default:
break;
}
@ -905,6 +914,19 @@ static const char *h2_conf_set_padding(cmd_parms *cmd, void *dirconf, const char
return NULL;
}
static const char *h2_conf_set_output_buffer(cmd_parms *cmd,
void *dirconf, const char *value)
{
if (!strcasecmp(value, "On")) {
CONFIG_CMD_SET(cmd, dirconf, H2_CONF_OUTPUT_BUFFER, 1);
return NULL;
}
else if (!strcasecmp(value, "Off")) {
CONFIG_CMD_SET(cmd, dirconf, H2_CONF_OUTPUT_BUFFER, 0);
return NULL;
}
return "value must be On or Off";
}
void h2_get_num_workers(server_rec *s, int *minw, int *maxw)
{
@ -976,6 +998,8 @@ const command_rec h2_cmds[] = {
RSRC_CONF, "on to enable interim status 103 responses"),
AP_INIT_TAKE1("H2Padding", h2_conf_set_padding, NULL,
RSRC_CONF, "set payload padding"),
AP_INIT_TAKE1("H2OutputBuffering", h2_conf_set_output_buffer, NULL,
RSRC_CONF, "set stream output buffer on/off"),
AP_END_CMD
};

View File

@ -44,6 +44,7 @@ typedef enum {
H2_CONF_EARLY_HINTS,
H2_CONF_PADDING_BITS,
H2_CONF_PADDING_ALWAYS,
H2_CONF_OUTPUT_BUFFER,
} h2_config_var_t;
struct apr_hash_t;

View File

@ -749,6 +749,7 @@ static int h2_h2_late_fixups(request_rec *r)
if (task) {
/* check if we copy vs. setaside files in this location */
task->output.copy_files = h2_config_rgeti(r, H2_CONF_COPY_FILES);
task->output.buffered = h2_config_rgeti(r, H2_CONF_OUTPUT_BUFFER);
if (task->output.copy_files) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c,
"h2_secondary_out(%s): copy_files on", task->id);

View File

@ -64,6 +64,7 @@ apr_bucket * h2_bucket_headers_make(apr_bucket *b, h2_headers *r)
b = apr_bucket_shared_make(b, br, 0, 0);
b->type = &h2_bucket_type_headers;
b->length = h2_headers_length(r);
return b;
}
@ -125,6 +126,20 @@ h2_headers *h2_headers_create(int status, apr_table_t *headers_in,
return headers;
}
static int add_header_lengths(void *ctx, const char *name, const char *value)
{
apr_size_t *plen = ctx;
*plen += strlen(name) + strlen(value);
return 1;
}
apr_size_t h2_headers_length(h2_headers *headers)
{
apr_size_t len = 0;
apr_table_do(add_header_lengths, &len, headers->headers, NULL);
return len;
}
h2_headers *h2_headers_rcreate(request_rec *r, int status,
apr_table_t *header, apr_pool_t *pool)
{

View File

@ -81,4 +81,9 @@ h2_headers *h2_headers_die(apr_status_t type,
int h2_headers_are_response(h2_headers *headers);
/**
* Give the number of bytes of all contained header strings.
*/
apr_size_t h2_headers_length(h2_headers *headers);
#endif /* defined(__mod_h2__h2_headers__) */

View File

@ -91,10 +91,6 @@ apr_status_t h2_mplx_m_child_init(apr_pool_t *pool, server_rec *s)
static void mst_check_data_for(h2_mplx *m, h2_stream *stream, int mplx_is_locked);
static void mst_stream_output_consumed(void *ctx, h2_bucket_beam *beam, apr_off_t length)
{
}
static void mst_stream_input_ev(void *ctx, h2_bucket_beam *beam)
{
h2_stream *stream = ctx;
@ -299,18 +295,6 @@ static int m_stream_destroy_iter(void *ctx, void *val)
stream->task = NULL;
secondary = task->c;
if (secondary) {
/* On non-serialized requests, the IO logging has not accounted for any
* meta data send over the network: response headers and h2 frame headers. we
* counted this on the stream and need to add this now.
* This is supposed to happen before the EOR bucket triggers the
* logging of the transaction. *fingers crossed* */
if (task->request && !task->request->serialize && h2_task_logio_add_bytes_out) {
apr_off_t unaccounted = stream->out_frame_octets - stream->out_data_octets;
if (unaccounted > 0) {
h2_task_logio_add_bytes_out(secondary, unaccounted);
}
}
if (m->s->keep_alive_max == 0 || secondary->keepalives < m->s->keep_alive_max) {
reuse_secondary = ((m->spare_secondary->nelts < (m->limit_active * 3 / 2))
&& !task->rst_error);
@ -540,7 +524,6 @@ static apr_status_t t_out_open(h2_mplx *m, int stream_id, h2_bucket_beam *beam)
"h2_mplx(%s): out open", stream->task->id);
}
h2_beam_on_consumed(stream->output, NULL, mst_stream_output_consumed, stream);
h2_beam_on_produced(stream->output, mst_output_produced, stream);
if (stream->task->output.copy_files) {
h2_beam_on_file_beam(stream->output, h2_beam_no_files, NULL);

View File

@ -278,11 +278,12 @@ request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c)
request_rec *r = my_ap_create_request(c);
#endif
#if AP_MODULE_MAGIC_AT_LEAST(20200331, 3)
ap_run_pre_read_request(r, c);
/* Time to populate r with the data we have. */
r->request_time = req->request_time;
r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0",
r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0",
req->method, req->path ? req->path : "");
r->headers_in = apr_table_clone(r->pool, req->headers);
@ -306,7 +307,50 @@ request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c)
r->status = HTTP_OK;
goto die;
}
#else
{
const char *s;
r->headers_in = apr_table_clone(r->pool, req->headers);
ap_run_pre_read_request(r, c);
/* Time to populate r with the data we have. */
r->request_time = req->request_time;
r->method = apr_pstrdup(r->pool, req->method);
/* Provide quick information about the request method as soon as known */
r->method_number = ap_method_number_of(r->method);
if (r->method_number == M_GET && r->method[0] == 'H') {
r->header_only = 1;
}
ap_parse_uri(r, req->path ? req->path : "");
r->protocol = (char*)"HTTP/2.0";
r->proto_num = HTTP_VERSION(2, 0);
r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0",
r->method, req->path ? req->path : "");
/* Start with r->hostname = NULL, ap_check_request_header() will get it
* form Host: header, otherwise we get complains about port numbers.
*/
r->hostname = NULL;
ap_update_vhost_from_headers(r);
/* we may have switched to another server */
r->per_dir_config = r->server->lookup_defaults;
s = apr_table_get(r->headers_in, "Expect");
if (s && s[0]) {
if (ap_cstr_casecmp(s, "100-continue") == 0) {
r->expecting_100 = 1;
}
else {
r->status = HTTP_EXPECTATION_FAILED;
access_status = r->status;
goto die;
}
}
}
#endif
/* we may have switched to another server */
r->per_dir_config = r->server->lookup_defaults;
@ -350,11 +394,19 @@ die:
*/
{
apr_bucket_brigade *eor_bb;
#if AP_MODULE_MAGIC_AT_LEAST(20180905, 1)
eor_bb = ap_acquire_brigade(c);
APR_BRIGADE_INSERT_TAIL(eor_bb,
ap_bucket_eor_create(c->bucket_alloc, r));
ap_pass_brigade(c->output_filters, eor_bb);
ap_release_brigade(c, eor_bb);
#else
eor_bb = apr_brigade_create(c->pool, c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(eor_bb,
ap_bucket_eor_create(c->bucket_alloc, r));
ap_pass_brigade(c->output_filters, eor_bb);
apr_brigade_destroy(eor_bb);
#endif
}
r = NULL;

View File

@ -92,7 +92,8 @@ struct h2_stream {
unsigned int input_eof : 1; /* no more request data coming */
unsigned int out_checked : 1; /* output eof was double checked */
unsigned int push_policy; /* which push policy to use for this request */
unsigned int input_buffering : 1; /* buffer request bodies for efficiency */
struct h2_task *task; /* assigned task to fullfill request */
const h2_priority *pref_priority; /* preferred priority for this stream */

View File

@ -89,6 +89,14 @@ static apr_status_t open_output(h2_task *task)
return h2_mplx_t_out_open(task->mplx, task->stream_id, task->output.beam);
}
static void output_consumed(void *ctx, h2_bucket_beam *beam, apr_off_t length)
{
h2_task *task = ctx;
if (task && h2_task_logio_add_bytes_out) {
h2_task_logio_add_bytes_out(task->c, length);
}
}
static apr_status_t send_out(h2_task *task, apr_bucket_brigade* bb, int block)
{
apr_off_t written, left;
@ -108,9 +116,6 @@ static apr_status_t send_out(h2_task *task, apr_bucket_brigade* bb, int block)
status = APR_SUCCESS;
}
if (status == APR_SUCCESS) {
if (h2_task_logio_add_bytes_out) {
h2_task_logio_add_bytes_out(task->c, written);
}
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, task->c,
"h2_task(%s): send_out done", task->id);
}
@ -183,7 +188,9 @@ send:
}
}
if (APR_SUCCESS == rv && !task->output.opened && flush) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, task->c,
"h2_secondary_out(%s): buffered=%d", task->id, task->output.buffered);
if (APR_SUCCESS == rv && !task->output.opened && (flush || !task->output.buffered)) {
/* got a flush or could not write all, time to tell someone to read */
rv = open_output(task);
}
@ -598,7 +605,8 @@ apr_status_t h2_task_do(h2_task *task, apr_thread_t *thread, int worker_id)
h2_beam_buffer_size_set(task->output.beam, task->output.max_buffer);
h2_beam_send_from(task->output.beam, task->pool);
h2_beam_on_consumed(task->output.beam, NULL, output_consumed, task);
h2_ctx_create_for(c, task);
apr_table_setn(c->notes, H2_TASK_ID_NOTE, task->id);

View File

@ -71,6 +71,7 @@ struct h2_task {
unsigned int opened : 1;
unsigned int sent_response : 1;
unsigned int copy_files : 1;
unsigned int buffered : 1;
struct h2_response_parser *rparser;
apr_bucket_brigade *bb;
apr_size_t max_buffer;

View File

@ -27,7 +27,7 @@
* @macro
* Version number of the http2 module as c string
*/
#define MOD_HTTP2_VERSION "1.15.14"
#define MOD_HTTP2_VERSION "1.15.17"
/**
* @macro
@ -35,6 +35,7 @@
* release. This is a 24 bit number with 8 bits for major number, 8 bits
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
*/
#define MOD_HTTP2_VERSION_NUM 0x010f0e
#define MOD_HTTP2_VERSION_NUM 0x010f11
#endif /* mod_h2_h2_version_h */