Skip to content

Commit 149f6c7

Browse files
committed
*) mod_http2: update to version 2.0.35
New directive `H2MaxStreamErrors` to control how much bad behaviour by clients is tolerated before the connection is closed. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1927792 13f79535-47bb-0310-9956-ffa450edef68
1 parent bddcfb1 commit 149f6c7

File tree

9 files changed

+107
-16
lines changed

9 files changed

+107
-16
lines changed

changes-entries/h2_v2.0.35.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*) mod_http2: update to version 2.0.35
2+
New directive `H2MaxStreamErrors` to control how much bad behaviour
3+
by clients is tolerated before the connection is closed.
4+
[Stefan Eissing]

docs/manual/mod/mod_http2.xml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,4 +1168,31 @@ H2EarlyHint Link "</my.css>;rel=preload;as=style"
11681168
</usage>
11691169
</directivesynopsis>
11701170

1171+
<directivesynopsis>
1172+
<name>H2MaxStreamErrors</name>
1173+
<description>Maximum amount of client caused errors to tolerate</description>
1174+
<syntax>H2MaxStreamErrors <em>n</em></syntax>
1175+
<default>H2MaxStreamErrors 8</default>
1176+
<contextlist>
1177+
<context>server config</context>
1178+
<context>virtual host</context>
1179+
</contextlist>
1180+
<compatibility>Available in version 2.5.1 and later.</compatibility>
1181+
1182+
<usage>
1183+
<p>
1184+
<directive>H2MaxStreamErrors</directive> sets the maxmimum amount
1185+
of tolerated HTTP/2 stream errors caused by the client.
1186+
When exceeding this limit, the connection will be closed.
1187+
Stream errors are protocol violations on an individual HTTP/2
1188+
stream that do not necessitate a connection close by the
1189+
protocol specification, but can be a sign of malicious
1190+
activity by a client.
1191+
</p>
1192+
<p>
1193+
Set to 0 to tolerate faulty clients.
1194+
</p>
1195+
</usage>
1196+
</directivesynopsis>
1197+
11711198
</modulesynopsis>

modules/http2/h2_config.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ typedef struct h2_config {
7878
apr_interval_time_t stream_timeout;/* beam timeout */
7979
int max_data_frame_len; /* max # bytes in a single h2 DATA frame */
8080
int max_hd_block_len; /* max # bytes in a response header block */
81+
int max_stream_errors; /* max # of tolerated stream errors */
8182
int proxy_requests; /* act as forward proxy */
8283
int h2_websockets; /* if mod_h2 negotiating WebSockets */
8384
} h2_config;
@@ -119,6 +120,7 @@ static h2_config defconf = {
119120
-1, /* beam timeout */
120121
0, /* max DATA frame len, 0 == no extra limit */
121122
0, /* max header block len, 0 == no extra limit */
123+
8, /* max stream errors tolerated */
122124
0, /* forward proxy */
123125
0, /* WebSockets negotiation, enabled */
124126
};
@@ -168,6 +170,7 @@ void *h2_config_create_svr(apr_pool_t *pool, server_rec *s)
168170
conf->stream_timeout = DEF_VAL;
169171
conf->max_data_frame_len = DEF_VAL;
170172
conf->max_hd_block_len = DEF_VAL;
173+
conf->max_stream_errors = DEF_VAL;
171174
conf->proxy_requests = DEF_VAL;
172175
conf->h2_websockets = DEF_VAL;
173176
return conf;
@@ -220,6 +223,7 @@ static void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv)
220223
n->stream_timeout = H2_CONFIG_GET(add, base, stream_timeout);
221224
n->max_data_frame_len = H2_CONFIG_GET(add, base, max_data_frame_len);
222225
n->max_hd_block_len = H2_CONFIG_GET(add, base, max_hd_block_len);
226+
n->max_stream_errors = H2_CONFIG_GET(add, base, max_stream_errors);
223227
n->proxy_requests = H2_CONFIG_GET(add, base, proxy_requests);
224228
n->h2_websockets = H2_CONFIG_GET(add, base, h2_websockets);
225229
return n;
@@ -319,6 +323,9 @@ static apr_int64_t h2_srv_config_geti64(const h2_config *conf, h2_config_var_t v
319323
return H2_CONFIG_GET(conf, &defconf, h2_websockets);
320324
case H2_CONF_MAX_HEADER_BLOCK_LEN:
321325
return H2_CONFIG_GET(conf, &defconf, max_hd_block_len);
326+
case H2_CONF_MAX_STREAM_ERRORS:
327+
return H2_CONFIG_GET(conf, &defconf, max_stream_errors);
328+
322329
default:
323330
return DEF_VAL;
324331
}
@@ -389,6 +396,9 @@ static void h2_srv_config_seti(h2_config *conf, h2_config_var_t var, int val)
389396
break;
390397
case H2_CONF_MAX_HEADER_BLOCK_LEN:
391398
H2_CONFIG_SET(conf, max_hd_block_len, val);
399+
break;
400+
case H2_CONF_MAX_STREAM_ERRORS:
401+
H2_CONFIG_SET(conf, max_stream_errors, val);
392402
default:
393403
break;
394404
}
@@ -669,6 +679,17 @@ static const char *h2_conf_set_max_hd_block_len(cmd_parms *cmd,
669679
return NULL;
670680
}
671681

682+
static const char *h2_conf_set_max_stream_errors(cmd_parms *cmd,
683+
void *dirconf, const char *value)
684+
{
685+
int val = (int)apr_atoi64(value);
686+
if (val < 0) {
687+
return "value must be 0 or larger";
688+
}
689+
CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MAX_STREAM_ERRORS, val);
690+
return NULL;
691+
}
692+
672693
static const char *h2_conf_set_session_extra_files(cmd_parms *cmd,
673694
void *dirconf, const char *value)
674695
{
@@ -1092,6 +1113,8 @@ const command_rec h2_cmds[] = {
10921113
RSRC_CONF, "maximum number of bytes in a single HTTP/2 DATA frame"),
10931114
AP_INIT_TAKE1("H2MaxHeaderBlockLen", h2_conf_set_max_hd_block_len, NULL,
10941115
RSRC_CONF, "maximum number of bytes in a response header block"),
1116+
AP_INIT_TAKE1("H2MaxStreamErrors", h2_conf_set_max_stream_errors, NULL,
1117+
RSRC_CONF, "maximum number of flow control errors tolerated"),
10951118
AP_INIT_TAKE2("H2EarlyHint", h2_conf_add_early_hint, NULL,
10961119
OR_FILEINFO|OR_AUTHCFG, "add a a 'Link:' header for a 103 Early Hints response."),
10971120
AP_INIT_TAKE1("H2ProxyRequests", h2_conf_set_proxy_requests, NULL,

modules/http2/h2_config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ typedef enum {
4747
H2_CONF_PROXY_REQUESTS,
4848
H2_CONF_WEBSOCKETS,
4949
H2_CONF_MAX_HEADER_BLOCK_LEN,
50+
H2_CONF_MAX_STREAM_ERRORS,
5051
} h2_config_var_t;
5152

5253
struct apr_hash_t;

modules/http2/h2_mplx.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,8 +1086,9 @@ static void s_mplx_be_happy(h2_mplx *m, conn_rec *c, h2_conn_ctx_t *conn_ctx)
10861086
m->last_mood_change = now;
10871087
m->irritations_since = 0;
10881088
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
1089-
H2_MPLX_MSG(m, "mood update, increasing worker limit to %d"),
1090-
m->processing_limit);
1089+
H2_MPLX_MSG(m, "mood update, increasing worker limit"
1090+
"to %d, processing %d right now"),
1091+
m->processing_limit, m->processing_count);
10911092
}
10921093
}
10931094
}
@@ -1116,8 +1117,9 @@ static void m_be_annoyed(h2_mplx *m)
11161117
m->last_mood_change = now;
11171118
m->irritations_since = 0;
11181119
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c1,
1119-
H2_MPLX_MSG(m, "mood update, decreasing worker limit to %d"),
1120-
m->processing_limit);
1120+
H2_MPLX_MSG(m, "mood update, decreasing worker limit "
1121+
"to %d, processing %d right now"),
1122+
m->processing_limit, m->processing_count);
11211123
}
11221124
}
11231125
}
@@ -1141,6 +1143,7 @@ static int reset_is_acceptable(h2_stream *stream)
11411143
* The responses to such requests continue forever otherwise.
11421144
*
11431145
*/
1146+
if (stream->rst_error) return 0; /* errored stream. bad. */
11441147
if (!stream_is_running(stream)) return 1;
11451148
if (!(stream->id & 0x01)) return 1; /* stream initiated by us. acceptable. */
11461149
if (!stream->response) return 0; /* no response headers produced yet. bad. */

modules/http2/h2_session.c

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ static void transit(h2_session *session, const char *action,
6161
static void on_stream_state_enter(void *ctx, h2_stream *stream);
6262
static void on_stream_state_event(void *ctx, h2_stream *stream, h2_stream_event_t ev);
6363
static void on_stream_event(void *ctx, h2_stream *stream, h2_stream_event_t ev);
64+
static apr_status_t h2_session_shutdown(h2_session *session, int error,
65+
const char *msg, int force_close);
6466

6567
static int h2_session_status_from_apr_status(apr_status_t rv)
6668
{
@@ -290,6 +292,7 @@ static int on_stream_close_cb(nghttp2_session *ngh2, int32_t stream_id,
290292
"closing with err=%d %s"),
291293
(int)error_code, h2_protocol_err_description(error_code));
292294
h2_stream_rst(stream, error_code);
295+
h2_mplx_c1_client_rst(session->mplx, stream_id, stream);
293296
}
294297
}
295298
return 0;
@@ -608,7 +611,11 @@ static int on_frame_send_cb(nghttp2_session *ngh2,
608611
/* PUSH_PROMISE we report on the promised stream */
609612
stream_id = frame->push_promise.promised_stream_id;
610613
break;
611-
default:
614+
case NGHTTP2_RST_STREAM:
615+
if(frame->rst_stream.error_code == NGHTTP2_FLOW_CONTROL_ERROR)
616+
++session->stream_errors;
617+
break;
618+
default:
612619
break;
613620
}
614621

@@ -652,10 +659,11 @@ static int on_frame_not_send_cb(nghttp2_session *ngh2,
652659

653660
stream = get_stream(session, stream_id);
654661
h2_util_frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
655-
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c1,
656-
H2_SSSN_LOG(APLOGNO(10509), session,
657-
"not sent FRAME[%s], error %d: %s"),
658-
buffer, ngh2_err, nghttp2_strerror(ngh2_err));
662+
if (!stream || !stream->rst_error)
663+
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c1,
664+
H2_SSSN_LOG(APLOGNO(10509), session,
665+
"not sent FRAME[%s], error %d: %s"),
666+
buffer, ngh2_err, nghttp2_strerror(ngh2_err));
659667
if(stream) {
660668
h2_stream_rst(stream, NGHTTP2_PROTOCOL_ERROR);
661669
return 0;
@@ -968,6 +976,7 @@ apr_status_t h2_session_create(h2_session **psession, conn_rec *c, request_rec *
968976
session->max_stream_count = h2_config_sgeti(s, H2_CONF_MAX_STREAMS);
969977
session->max_stream_mem = h2_config_sgeti(s, H2_CONF_STREAM_MAX_MEM);
970978
session->max_data_frame_len = h2_config_sgeti(s, H2_CONF_MAX_DATA_FRAME_LEN);
979+
session->max_stream_errors = h2_config_sgeti(s, H2_CONF_MAX_STREAM_ERRORS);
971980

972981
session->out_c1_blocked = h2_iq_create(session->pool, (int)session->max_stream_count);
973982
session->ready_to_process = h2_iq_create(session->pool, (int)session->max_stream_count);
@@ -1063,14 +1072,16 @@ apr_status_t h2_session_create(h2_session **psession, conn_rec *c, request_rec *
10631072
"created, max_streams=%d, stream_mem=%d, "
10641073
"workers_limit=%d, workers_max=%d, "
10651074
"push_diary(type=%d,N=%d), "
1066-
"max_data_frame_len=%d"),
1075+
"max_data_frame_len=%d, "
1076+
"max_stream_errors=%d"),
10671077
(int)session->max_stream_count,
10681078
(int)session->max_stream_mem,
10691079
session->mplx->processing_limit,
10701080
session->mplx->processing_max,
10711081
session->push_diary->dtype,
10721082
(int)session->push_diary->N,
1073-
(int)session->max_data_frame_len);
1083+
(int)session->max_data_frame_len,
1084+
session->max_stream_errors);
10741085
}
10751086

10761087
apr_pool_pre_cleanup_register(pool, c, session_pool_cleanup);
@@ -1609,6 +1620,14 @@ static void h2_session_ev_mpm_stopping(h2_session *session, int arg, const char
16091620
}
16101621
}
16111622

1623+
static void h2_session_ev_bad_client(h2_session *session, int arg, const char *msg)
1624+
{
1625+
transit(session, msg, H2_SESSION_ST_DONE);
1626+
if (!session->local.shutdown) {
1627+
h2_session_shutdown(session, arg, msg, 1);
1628+
}
1629+
}
1630+
16121631
static void h2_session_ev_pre_close(h2_session *session, int arg, const char *msg)
16131632
{
16141633
h2_session_shutdown(session, arg, msg, 1);
@@ -1806,6 +1825,9 @@ void h2_session_dispatch_event(h2_session *session, h2_session_event_t ev,
18061825
case H2_SESSION_EV_NO_MORE_STREAMS:
18071826
h2_session_ev_no_more_streams(session);
18081827
break;
1828+
case H2_SESSION_EV_BAD_CLIENT:
1829+
h2_session_ev_bad_client(session, arg, msg);
1830+
break;
18091831
default:
18101832
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c1,
18111833
H2_SSSN_MSG(session, "unknown event %d"), ev);
@@ -1884,6 +1906,12 @@ apr_status_t h2_session_process(h2_session *session, int async,
18841906
}
18851907
}
18861908

1909+
if (session->max_stream_errors &&
1910+
session->stream_errors > session->max_stream_errors) {
1911+
h2_session_dispatch_event(session, H2_SESSION_EV_BAD_CLIENT,
1912+
NGHTTP2_PROTOCOL_ERROR, NULL);
1913+
}
1914+
18871915
session->status[0] = '\0';
18881916

18891917
if (h2_session_want_send(session)) {

modules/http2/h2_session.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ typedef enum {
6060
H2_SESSION_EV_MPM_STOPPING, /* the process is stopping */
6161
H2_SESSION_EV_PRE_CLOSE, /* connection will close after this */
6262
H2_SESSION_EV_NO_MORE_STREAMS, /* no more streams to process */
63+
H2_SESSION_EV_BAD_CLIENT, /* client misbehaving badly */
6364
} h2_session_event_t;
6465

6566
typedef struct h2_session {
@@ -98,7 +99,9 @@ typedef struct h2_session {
9899
unsigned int pushes_promised; /* number of http/2 push promises submitted */
99100
unsigned int pushes_submitted; /* number of http/2 pushed responses submitted */
100101
unsigned int pushes_reset; /* number of http/2 pushed reset by client */
101-
102+
unsigned int max_stream_errors; /* max client stream errors tolerated */
103+
unsigned int stream_errors; /* number of stream errors by client */
104+
102105
apr_size_t frames_received; /* number of http/2 frames received */
103106
apr_size_t frames_sent; /* number of http/2 frames sent */
104107

modules/http2/h2_util.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1805,9 +1805,11 @@ int h2_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
18051805
}
18061806
case NGHTTP2_RST_STREAM: {
18071807
return apr_snprintf(buffer, maxlen,
1808-
"RST_STREAM[length=%d, flags=%d, stream=%d]",
1808+
"RST_STREAM[length=%d, flags=%d, stream=%d"
1809+
",error=%d]",
18091810
(int)frame->hd.length,
1810-
frame->hd.flags, frame->hd.stream_id);
1811+
frame->hd.flags, frame->hd.stream_id,
1812+
frame->rst_stream.error_code);
18111813
}
18121814
case NGHTTP2_SETTINGS: {
18131815
if (frame->hd.flags & NGHTTP2_FLAG_ACK) {

modules/http2/h2_version.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@
2727
* @macro
2828
* Version number of the http2 module as c string
2929
*/
30-
#define MOD_HTTP2_VERSION "2.0.34"
30+
#define MOD_HTTP2_VERSION "2.0.35"
3131

3232
/**
3333
* @macro
3434
* Numerical representation of the version number of the http2 module
3535
* release. This is a 24 bit number with 8 bits for major number, 8 bits
3636
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
3737
*/
38-
#define MOD_HTTP2_VERSION_NUM 0x020022
38+
#define MOD_HTTP2_VERSION_NUM 0x020023
3939

4040

4141
#endif /* mod_h2_h2_version_h */

0 commit comments

Comments
 (0)