mirror of
https://github.com/apache/httpd.git
synced 2025-08-16 16:17:23 +00:00
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:
6
changes-entries/http2_logio.txt
Normal file
6
changes-entries/http2_logio.txt
Normal 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]
|
||||
|
5
changes-entries/http2_output_buffering.txt
Normal file
5
changes-entries/http2_output_buffering.txt
Normal 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]
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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__) */
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
Reference in New Issue
Block a user