--- /dev/null
+ *) mod_http2: update to version 2.0.30
+ - Fixed bug in handling over long response headers. When the 64 KB limit
+ of nghttp2 was exceeded, the request was not reset and the client was
+ left hanging, waiting for it. Now the stream is reset.
+ - Added new directive `H2MaxHeaderBlockLen` to set the limit on response
+ header sizes.
+ - Fixed handling of Timeout vs. KeepAliveTimeout when first request on a
+ connection was reset.
</usage>
</directivesynopsis>
+ <directivesynopsis>
+ <name>H2MaxHeaderBlockLen</name>
+ <description>Maximum size of response headers</description>
+ <syntax>H2MaxHeaderBlockLen <em>n</em></syntax>
+ <default>H2MaxHeaderBlockLen 0</default>
+ <contextlist>
+ <context>server config</context>
+ <context>virtual host</context>
+ </contextlist>
+ <compatibility>Available in version 2.4.64 and later.</compatibility>
+
+ <usage>
+ <p>
+ <directive>H2MaxHeaderBlockLen</directive> sets the limit
+ on the overall size of response headers. A setting of 0
+ will leave this at the default of 64 KB in nghttp2.
+ </p>
+ <p>
+ Responses with headers larger than this (adding all headers)
+ will not be processed and result in a reset of the stream.
+ </p>
+ </usage>
+ </directivesynopsis>
+
</modulesynopsis>
int output_buffered;
apr_interval_time_t stream_timeout;/* beam timeout */
int max_data_frame_len; /* max # bytes in a single h2 DATA frame */
+ int max_hd_block_len; /* max # bytes in a response header block */
int proxy_requests; /* act as forward proxy */
int h2_websockets; /* if mod_h2 negotiating WebSockets */
} h2_config;
1, /* stream output buffered */
-1, /* beam timeout */
0, /* max DATA frame len, 0 == no extra limit */
+ 0, /* max header block len, 0 == no extra limit */
0, /* forward proxy */
0, /* WebSockets negotiation, enabled */
};
conf->output_buffered = DEF_VAL;
conf->stream_timeout = DEF_VAL;
conf->max_data_frame_len = DEF_VAL;
+ conf->max_hd_block_len = DEF_VAL;
conf->proxy_requests = DEF_VAL;
conf->h2_websockets = DEF_VAL;
return conf;
n->padding_always = H2_CONFIG_GET(add, base, padding_always);
n->stream_timeout = H2_CONFIG_GET(add, base, stream_timeout);
n->max_data_frame_len = H2_CONFIG_GET(add, base, max_data_frame_len);
+ n->max_hd_block_len = H2_CONFIG_GET(add, base, max_hd_block_len);
n->proxy_requests = H2_CONFIG_GET(add, base, proxy_requests);
n->h2_websockets = H2_CONFIG_GET(add, base, h2_websockets);
return n;
return H2_CONFIG_GET(conf, &defconf, proxy_requests);
case H2_CONF_WEBSOCKETS:
return H2_CONFIG_GET(conf, &defconf, h2_websockets);
+ case H2_CONF_MAX_HEADER_BLOCK_LEN:
+ return H2_CONFIG_GET(conf, &defconf, max_hd_block_len);
default:
return DEF_VAL;
}
case H2_CONF_WEBSOCKETS:
H2_CONFIG_SET(conf, h2_websockets, val);
break;
+ case H2_CONF_MAX_HEADER_BLOCK_LEN:
+ H2_CONFIG_SET(conf, max_hd_block_len, val);
default:
break;
}
return NULL;
}
+static const char *h2_conf_set_max_hd_block_len(cmd_parms *cmd,
+ void *dirconf, const char *value)
+{
+ int val = (int)apr_atoi64(value);
+ if (val < 0) {
+ return "value must be 0 or larger";
+ }
+ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MAX_HEADER_BLOCK_LEN, val);
+ return NULL;
+}
+
static const char *h2_conf_set_session_extra_files(cmd_parms *cmd,
void *dirconf, const char *value)
{
RSRC_CONF, "set stream timeout"),
AP_INIT_TAKE1("H2MaxDataFrameLen", h2_conf_set_max_data_frame_len, NULL,
RSRC_CONF, "maximum number of bytes in a single HTTP/2 DATA frame"),
+ AP_INIT_TAKE1("H2MaxHeaderBlockLen", h2_conf_set_max_hd_block_len, NULL,
+ RSRC_CONF, "maximum number of bytes in a response header block"),
AP_INIT_TAKE2("H2EarlyHint", h2_conf_add_early_hint, NULL,
OR_FILEINFO|OR_AUTHCFG, "add a a 'Link:' header for a 103 Early Hints response."),
AP_INIT_TAKE1("H2ProxyRequests", h2_conf_set_proxy_requests, NULL,
H2_CONF_MAX_DATA_FRAME_LEN,
H2_CONF_PROXY_REQUESTS,
H2_CONF_WEBSOCKETS,
+ H2_CONF_MAX_HEADER_BLOCK_LEN,
} h2_config_var_t;
struct apr_hash_t;
return 0;
}
+static int on_frame_not_send_cb(nghttp2_session *ngh2,
+ const nghttp2_frame *frame,
+ int ngh2_err,
+ void *user_data)
+{
+ h2_session *session = user_data;
+ int stream_id = frame->hd.stream_id;
+ h2_stream *stream;
+ char buffer[256];
+
+ stream = get_stream(session, stream_id);
+ h2_util_frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c1,
+ H2_SSSN_LOG(APLOGNO(10509), session,
+ "not sent FRAME[%s], error %d: %s"),
+ buffer, ngh2_err, nghttp2_strerror(ngh2_err));
+ if(stream) {
+ h2_stream_rst(stream, NGHTTP2_PROTOCOL_ERROR);
+ return 0;
+ }
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+}
+
#ifdef H2_NG2_INVALID_HEADER_CB
static int on_invalid_header_cb(nghttp2_session *ngh2,
const nghttp2_frame *frame,
NGH2_SET_CALLBACK(*pcb, on_header, on_header_cb);
NGH2_SET_CALLBACK(*pcb, send_data, on_send_data_cb);
NGH2_SET_CALLBACK(*pcb, on_frame_send, on_frame_send_cb);
+ NGH2_SET_CALLBACK(*pcb, on_frame_not_send, on_frame_not_send_cb);
#ifdef H2_NG2_INVALID_HEADER_CB
NGH2_SET_CALLBACK(*pcb, on_invalid_header, on_invalid_header_cb);
#endif
* handle them, just like the HTTP/1.1 parser does. */
nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(options, 1);
#endif
+
+ if(h2_config_sgeti(s, H2_CONF_MAX_HEADER_BLOCK_LEN) > 0)
+ nghttp2_option_set_max_send_header_block_length(options,
+ h2_config_sgeti(s, H2_CONF_MAX_HEADER_BLOCK_LEN));
+
rv = nghttp2_session_server_new2(&session->ngh2, callbacks,
session, options);
nghttp2_session_callbacks_del(callbacks);
if (nghttp2_is_fatal(ngrv)) {
rv = APR_EGENERAL;
h2_session_dispatch_event(stream->session,
- H2_SESSION_EV_PROTO_ERROR, ngrv, nghttp2_strerror(rv));
+ H2_SESSION_EV_PROTO_ERROR, ngrv, nghttp2_strerror(ngrv));
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c1,
APLOGNO(10402) "submit_response: %s",
- nghttp2_strerror(rv));
+ nghttp2_strerror(ngrv));
goto cleanup;
}
* @macro
* Version number of the http2 module as c string
*/
-#define MOD_HTTP2_VERSION "2.0.27"
+#define MOD_HTTP2_VERSION "2.0.30"
/**
* @macro
* 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 0x02001b
+#define MOD_HTTP2_VERSION_NUM 0x02001e
#endif /* mod_h2_h2_version_h */